{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4c96485e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings('ignore')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "fb1c8b6f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import gc\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import re\n",
    "import time\n",
    "from scipy import stats\n",
    "import matplotlib.pyplot as plt\n",
    "import category_encoders as ce\n",
    "import networkx as nx\n",
    "import pickle\n",
    "import lightgbm as lgb\n",
    "import catboost as cat\n",
    "import xgboost as xgb\n",
    "import seaborn as sns\n",
    "from datetime import timedelta\n",
    "from gensim.models import Word2Vec\n",
    "from io import StringIO\n",
    "from tqdm import tqdm\n",
    "from lightgbm import LGBMClassifier\n",
    "from lightgbm import log_evaluation, early_stopping\n",
    "from sklearn.metrics import roc_curve\n",
    "from scipy.stats import chi2_contingency, pearsonr\n",
    "from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer\n",
    "from sklearn.feature_extraction import FeatureHasher\n",
    "from sklearn.model_selection import StratifiedKFold, KFold, train_test_split, GridSearchCV\n",
    "from category_encoders import TargetEncoder\n",
    "from sklearn.decomposition import TruncatedSVD\n",
    "from autogluon.tabular import TabularDataset, TabularPredictor, FeatureMetadata\n",
    "from autogluon.features.generators import AsTypeFeatureGenerator, BulkFeatureGenerator, DropUniqueFeatureGenerator, FillNaFeatureGenerator, PipelineFeatureGenerator\n",
    "from autogluon.features.generators import CategoryFeatureGenerator, IdentityFeatureGenerator, AutoMLPipelineFeatureGenerator\n",
    "from autogluon.common.features.types import R_INT, R_FLOAT\n",
    "from autogluon.core.metrics import make_scorer"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e71fa04",
   "metadata": {},
   "source": [
    "# 数据导入"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b8030ea",
   "metadata": {},
   "source": [
    "## 数据导入通用函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ea884f9b",
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_data_from_directory(directory):\n",
    "    \"\"\"\n",
    "    遍历目录加载所有CSV文件，将其作为独立的DataFrame变量\n",
    "\n",
    "    参数:\n",
    "    - directory: 输入的数据路径\n",
    "    \n",
    "    返回:\n",
    "    - 含有数据集名称的列表\n",
    "    \"\"\"\n",
    "    dataset_names = []\n",
    "    for filename in os.listdir(directory):\n",
    "        if filename.endswith(\".csv\"):\n",
    "            dataset_name = os.path.splitext(filename)[0] + '_data' # 获取文件名作为变量名\n",
    "            file_path = os.path.join(directory, filename)  # 完整的文件路径\n",
    "            globals()[dataset_name] = pd.read_csv(file_path)  # 将文件加载为DataFrame并赋值给全局变量\n",
    "            dataset_names.append(dataset_name)\n",
    "            print(f\"数据集 {dataset_name} 已加载为 DataFrame\")\n",
    "\n",
    "    return dataset_names"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2173f06",
   "metadata": {},
   "source": [
    "## 训练集导入"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "63ed3faf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "数据集 TRAIN_ASSET_data 已加载为 DataFrame\n",
      "数据集 TRAIN_CCD_TR_DTL_data 已加载为 DataFrame\n",
      "数据集 TRAIN_MB_CUST_INFO_data 已加载为 DataFrame\n",
      "数据集 TRAIN_MB_PAGEVIEW_DTL_data 已加载为 DataFrame\n",
      "数据集 TRAIN_MB_TRNFLW_DTL_data 已加载为 DataFrame\n",
      "数据集 TRAIN_NATURE_data 已加载为 DataFrame\n",
      "数据集 TRAIN_PROD_HOLD_data 已加载为 DataFrame\n",
      "数据集 TRAIN_TR_APS_DTL_data 已加载为 DataFrame\n"
     ]
    }
   ],
   "source": [
    "train_load_dt = './data/Train'\n",
    "train_data_name = load_data_from_directory(train_load_dt)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61c6829d",
   "metadata": {},
   "source": [
    "## 测试集导入"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "7ea8c6d1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "数据集 A_ASSET_data 已加载为 DataFrame\n",
      "数据集 A_CCD_TR_DTL_data 已加载为 DataFrame\n",
      "数据集 A_MB_CUST_INFO_data 已加载为 DataFrame\n",
      "数据集 A_MB_PAGEVIEW_DTL_data 已加载为 DataFrame\n",
      "数据集 A_MB_TRNFLW_DTL_data 已加载为 DataFrame\n",
      "数据集 A_PROD_HOLD_data 已加载为 DataFrame\n",
      "数据集 A_TEST_NATURE_data 已加载为 DataFrame\n",
      "数据集 A_TR_APS_DTL_data 已加载为 DataFrame\n"
     ]
    }
   ],
   "source": [
    "A_load_dt = './data/A'\n",
    "A_data_name = load_data_from_directory(A_load_dt)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a0dee6c",
   "metadata": {},
   "source": [
    "# 特征工程"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d48d664",
   "metadata": {},
   "source": [
    "## 1. 自然属性信息表特征工程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1d8f99fe",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "====================================================================================================\n",
      "开始处理自然属性信息表 (NATURE)\n",
      "====================================================================================================\n",
      "\n",
      "处理训练集:\n",
      "原始数据维度: (1000, 10)\n",
      "\n",
      "【数据预处理】\n",
      "  AGE字段缺失值: 0\n",
      "\n",
      "【类别字段分布分析】\n",
      "  性别类别: [1, 2], 分布: {2: 572, 1: 428}\n",
      "  婚姻状况类别: [1.0, 2.0, 4.0, 9.0], 分布: {2.0: 318, 9.0: 247, 1.0: 58, 4.0: 10}\n",
      "  学历类别: ['20', '30', '40', '50', '60', '70', '80', '90', 'A0', 'C0', 'ZY'], 分布: {'ZY': 346, '60': 80, '70': 58, '20': 45, '30': 32, 'C0': 15, '40': 8, '80': 6, 'A0': 1, '90': 1, '50': 1}\n",
      "  客户建立时间类别数: 684\n",
      "  客户价值等级类别: [1, 2, 3, 4, 5, 6, 7], 分布: {2: 390, 5: 184, 1: 136, 4: 130, 3: 125, 6: 32, 7: 3}\n",
      "\n",
      "处理年龄特征...\n",
      "处理性别特征...\n",
      "  性别类别数: 2\n",
      "处理婚姻状况特征...\n",
      "  婚姻状况类别数: 4\n",
      "处理学历特征...\n",
      "  学历类别数: 11\n",
      "处理客户建立时间特征...\n",
      "处理客户价值等级特征...\n",
      "  客户价值等级类别数: 7\n",
      "处理持卡数量特征...\n",
      "生成年龄与性别交互特征...\n",
      "生成年龄与价值等级交互特征...\n",
      "生成其他交互特征...\n",
      "生成客户画像特征...\n",
      "生成客户潜力评分...\n",
      "\n",
      "处理后特征维度: (1000, 91)\n",
      "新增特征数: 89\n",
      "\n",
      "====================================================================================================\n",
      "\n",
      "处理测试集:\n",
      "原始数据维度: (500, 10)\n",
      "\n",
      "【数据预处理】\n",
      "  AGE字段缺失值: 1\n",
      "  已将AGE缺失值填充为中位数: 46.0\n",
      "\n",
      "【类别字段分布分析】\n",
      "  性别类别: [1.0, 2.0], 分布: {2.0: 294, 1.0: 205}\n",
      "  婚姻状况类别: [1.0, 2.0, 4.0, 9.0], 分布: {2.0: 163, 9.0: 136, 1.0: 26, 4.0: 2}\n",
      "  学历类别: ['20', '30', '40', '60', '70', '80', 'C0', 'ZY'], 分布: {'ZY': 198, '60': 42, '70': 24, '30': 20, '20': 15, '40': 7, 'C0': 4, '80': 4}\n",
      "  客户建立时间类别数: 375\n",
      "  客户价值等级类别: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], 分布: {2.0: 185, 5.0: 97, 4.0: 72, 1.0: 64, 3.0: 64, 6.0: 14, 7.0: 3}\n",
      "\n",
      "处理年龄特征...\n",
      "处理性别特征...\n",
      "  性别类别数: 2\n",
      "处理婚姻状况特征...\n",
      "  婚姻状况类别数: 4\n",
      "处理学历特征...\n",
      "  学历类别数: 8\n",
      "处理客户建立时间特征...\n",
      "处理客户价值等级特征...\n",
      "  客户价值等级类别数: 7\n",
      "处理持卡数量特征...\n",
      "生成年龄与性别交互特征...\n",
      "生成年龄与价值等级交互特征...\n",
      "生成其他交互特征...\n",
      "生成客户画像特征...\n",
      "生成客户潜力评分...\n",
      "\n",
      "处理后特征维度: (500, 88)\n",
      "新增特征数: 86\n",
      "\n",
      "====================================================================================================\n",
      "自然属性信息表特征工程完成!\n",
      "====================================================================================================\n"
     ]
    }
   ],
   "source": [
    "print(\"=\"*100)\n",
    "print(\"开始处理自然属性信息表 (NATURE)\")\n",
    "print(\"=\"*100)\n",
    "\n",
    "# 训练集\n",
    "TRAIN_NATURE_features = TRAIN_NATURE_data.copy()\n",
    "# 测试集\n",
    "A_NATURE_features = A_TEST_NATURE_data.copy()\n",
    "\n",
    "def process_nature_features(df):\n",
    "    \"\"\"\n",
    "    自然属性信息表特征工程\n",
    "    包含: 年龄、性别、婚姻状况、学历、客户建立时间、客户价值等级、持卡数量\n",
    "    \"\"\"\n",
    "    print(f\"原始数据维度: {df.shape}\")\n",
    "    \n",
    "    features = df.copy()\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 1. 类别字段探查与分析及数据类型转换\n",
    "    # ============================================================================\n",
    "    print(\"\\n【类别字段分布分析】\")\n",
    "    \n",
    "    # 1.1 性别字段 - 字母类型转数字\n",
    "    print(\"  处理性别字段...\")\n",
    "    sex_mapping = {'A': 1, 'B': 2, 'C': 3}  # A/B/C转换为1/2/3\n",
    "    features['IDV_CUST_SEX'] = features['IDV_CUST_SEX'].map(sex_mapping).fillna(0).astype(int)  # 缺失值用0填充\n",
    "    sex_unique = sorted([x for x in features['IDV_CUST_SEX'].unique()])\n",
    "    sex_counts = features['IDV_CUST_SEX'].value_counts()\n",
    "    print(f\"    性别类别(已转换): {sex_unique}, 分布: {dict(sex_counts)}\")\n",
    "    \n",
    "    # 1.2 婚姻状况字段 - 字母类型转数字\n",
    "    print(\"  处理婚姻状况字段...\")\n",
    "    marriage_mapping = {'A': 1, 'B': 2, 'C': 3}  # A/B/C转换为1/2/3\n",
    "    features['IDV_CUST_MRGE_STS'] = features['IDV_CUST_MRGE_STS'].map(marriage_mapping).fillna(0).astype(int)  # 缺失值用0填充\n",
    "    marriage_unique = sorted([x for x in features['IDV_CUST_MRGE_STS'].unique()])\n",
    "    marriage_counts = features['IDV_CUST_MRGE_STS'].value_counts()\n",
    "    print(f\"    婚姻状况类别(已转换): {marriage_unique}, 分布: {dict(marriage_counts)}\")\n",
    "    \n",
    "    # 1.3 学历字段 - MD5脱敏数据\n",
    "    print(\"  处理学历字段(MD5脱敏数据)...\")\n",
    "    # 学历字段是MD5脱敏后的哈希值,不需要转换,保持原样\n",
    "    features['IDV_CUST_HEDU'] = features['IDV_CUST_HEDU'].fillna('UNKNOWN').astype(str)  # 缺失值用'UNKNOWN'填充\n",
    "    edu_unique = sorted([str(x) for x in features['IDV_CUST_HEDU'].unique()])\n",
    "    edu_counts = features['IDV_CUST_HEDU'].value_counts()\n",
    "    print(f\"    学历类别数(MD5哈希值): {len(edu_unique)}, 前5个: {edu_unique[:5]}\")\n",
    "    \n",
    "    # 1.4 客户建立时间字段 - float64转日期类型\n",
    "    print(\"  处理客户建立时间字段...\")\n",
    "    # 将float64类型转换为整数字符串(如19911231.0 -> '19911231')\n",
    "    features['IDV_CUST_CRT_TIME'] = features['IDV_CUST_CRT_TIME'].fillna(0).astype(int).astype(str).replace('0', '19000101')\n",
    "    # 转换为日期类型\n",
    "    features['IDV_CUST_CRT_TIME'] = pd.to_datetime(features['IDV_CUST_CRT_TIME'], format='%Y%m%d', errors='coerce')\n",
    "    # 对于无法转换的日期,用一个默认日期填充\n",
    "    features['IDV_CUST_CRT_TIME'] = features['IDV_CUST_CRT_TIME'].fillna(pd.to_datetime('19000101', format='%Y%m%d'))\n",
    "    cust_time_unique = features['IDV_CUST_CRT_TIME'].unique()\n",
    "    print(f\"    客户建立时间类别数: {len(cust_time_unique)}, 日期范围: {features['IDV_CUST_CRT_TIME'].min()} ~ {features['IDV_CUST_CRT_TIME'].max()}\")\n",
    "    \n",
    "    # 1.5 客户价值等级字段 - 字母类型转数字\n",
    "    print(\"  处理客户价值等级字段...\")\n",
    "    value_mapping = {'A': 1, 'B': 2, 'C': 3}  # A/B/C转换为1/2/3\n",
    "    features['IDV_CUST_VLU_RANK'] = features['IDV_CUST_VLU_RANK'].map(value_mapping).fillna(0).astype(int)  # 缺失值用0填充\n",
    "    value_rank_unique = sorted([x for x in features['IDV_CUST_VLU_RANK'].unique()])\n",
    "    value_rank_counts = features['IDV_CUST_VLU_RANK'].value_counts()\n",
    "    print(f\"    客户价值等级类别(已转换): {value_rank_unique}, 分布: {dict(value_rank_counts)}\")\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 2. 年龄特征工程\n",
    "    # ============================================================================\n",
    "    print(\"\\n处理年龄特征...\")\n",
    "    \n",
    "    # 年龄分段 - 细分\n",
    "    features['NATURE_AGE_GROUP_FINE'] = pd.cut(\n",
    "        features['AGE'], \n",
    "        bins=[0, 22, 28, 35, 42, 50, 60, 150], \n",
    "        labels=[1, 2, 3, 4, 5, 6, 7]\n",
    "    ).astype(int)\n",
    "    \n",
    "    # 年龄分段 - 粗分\n",
    "    features['NATURE_AGE_GROUP_COARSE'] = pd.cut(\n",
    "        features['AGE'], \n",
    "        bins=[0, 30, 45, 60, 150], \n",
    "        labels=[1, 2, 3, 4]\n",
    "    ).astype(int)\n",
    "    \n",
    "    # 年龄的非线性变换\n",
    "    features['NATURE_AGE_SQUARE'] = features['AGE'] ** 2\n",
    "    features['NATURE_AGE_CUBE'] = features['AGE'] ** 3\n",
    "    features['NATURE_AGE_SQRT'] = np.sqrt(features['AGE'])\n",
    "    features['NATURE_AGE_LOG'] = np.log1p(features['AGE'])\n",
    "    \n",
    "    # 年龄标准化\n",
    "    features['NATURE_AGE_NORMALIZED'] = (features['AGE'] - features['AGE'].mean()) / (features['AGE'].std() + 1e-5)\n",
    "    \n",
    "    # 年龄分位数\n",
    "    features['NATURE_AGE_RANK'] = features['AGE'].rank(pct=True)\n",
    "    \n",
    "    # 是否特定年龄段\n",
    "    features['NATURE_IS_YOUNG'] = (features['AGE'] <= 30).astype(int)  # 青年\n",
    "    features['NATURE_IS_MIDDLE'] = ((features['AGE'] > 30) & (features['AGE'] <= 50)).astype(int)  # 中年\n",
    "    features['NATURE_IS_OLD'] = (features['AGE'] > 50).astype(int)  # 老年\n",
    "    features['NATURE_IS_PRIME_AGE'] = ((features['AGE'] >= 25) & (features['AGE'] <= 45)).astype(int)  # 黄金年龄段\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 3. 性别特征工程\n",
    "    # ============================================================================\n",
    "    print(\"处理性别特征...\")\n",
    "    \n",
    "    # 保留已转换的性别编码(已经是数字类型,0表示缺失值)\n",
    "    features['NATURE_SEX'] = features['IDV_CUST_SEX']\n",
    "    \n",
    "    # 性别类别数量\n",
    "    sex_num_categories = len(sex_unique)\n",
    "    print(f\"  性别类别数: {sex_num_categories}\")\n",
    "    \n",
    "    # 为每个性别类别生成标识特征\n",
    "    for cat in sex_unique:\n",
    "        if cat != 0:  # 排除缺失值标识\n",
    "            features[f'NATURE_IS_SEX_{cat}'] = (features['NATURE_SEX'] == cat).astype(int)\n",
    "    \n",
    "    # 是否缺失性别信息\n",
    "    features['NATURE_SEX_MISSING'] = (features['NATURE_SEX'] == 0).astype(int)\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 4. 婚姻状况特征工程\n",
    "    # ============================================================================\n",
    "    print(\"处理婚姻状况特征...\")\n",
    "    \n",
    "    # 保留已转换的婚姻状况编码(已经是数字类型,0表示缺失值)\n",
    "    features['NATURE_MARRIAGE'] = features['IDV_CUST_MRGE_STS']\n",
    "    \n",
    "    # 婚姻状况类别数量\n",
    "    marriage_num_categories = len(marriage_unique)\n",
    "    print(f\"  婚姻状况类别数: {marriage_num_categories}\")\n",
    "    \n",
    "    # 根据实际类别生成标识特征\n",
    "    for cat in marriage_unique:\n",
    "        if cat != 0:  # 排除缺失值标识\n",
    "            features[f'NATURE_IS_MARRIAGE_{cat}'] = (features['NATURE_MARRIAGE'] == cat).astype(int)\n",
    "    \n",
    "    # 是否缺失婚姻状况信息\n",
    "    features['NATURE_MARRIAGE_MISSING'] = (features['NATURE_MARRIAGE'] == 0).astype(int)\n",
    "    \n",
    "    # 通用婚姻状态标识(兼容不同编码方式)\n",
    "    # 假设1为未婚，2为已婚，3为离异/丧偶\n",
    "    valid_categories = [c for c in marriage_unique if c != 0]\n",
    "    if len(valid_categories) >= 2:\n",
    "        min_marriage = min(valid_categories)\n",
    "        max_marriage = max(valid_categories)\n",
    "        features['NATURE_IS_SINGLE'] = (features['NATURE_MARRIAGE'] == min_marriage).astype(int)\n",
    "        features['NATURE_IS_MARRIED'] = (features['NATURE_MARRIAGE'] == max_marriage).astype(int)\n",
    "        \n",
    "        # 如果有3个或以上类别，中间值可能是离异/丧偶等\n",
    "        if len(valid_categories) >= 3:\n",
    "            middle_marriage = [m for m in valid_categories if m != min_marriage and m != max_marriage]\n",
    "            features['NATURE_IS_DIVORCED_OR_WIDOWED'] = features['NATURE_MARRIAGE'].isin(middle_marriage).astype(int)\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 5. 学历特征工程 (MD5脱敏数据)\n",
    "    # ============================================================================\n",
    "    print(\"处理学历特征(MD5脱敏数据)...\")\n",
    "    \n",
    "    # 保留学历MD5哈希值\n",
    "    features['NATURE_EDU'] = features['IDV_CUST_HEDU']\n",
    "    \n",
    "    # 学历类别数量\n",
    "    edu_num_categories = len(edu_unique)\n",
    "    print(f\"  学历类别数(MD5哈希): {edu_num_categories}\")\n",
    "    \n",
    "    # 是否缺失学历信息\n",
    "    features['NATURE_EDU_MISSING'] = (features['NATURE_EDU'] == 'UNKNOWN').astype(int)\n",
    "    \n",
    "    # 使用LabelEncoder将MD5哈希值编码为数字\n",
    "    from sklearn.preprocessing import LabelEncoder\n",
    "    le = LabelEncoder()\n",
    "    features['NATURE_EDU_ENCODED'] = le.fit_transform(features['NATURE_EDU'])\n",
    "    print(f\"  学历编码范围: {features['NATURE_EDU_ENCODED'].min()} ~ {features['NATURE_EDU_ENCODED'].max()}\")\n",
    "    \n",
    "    # 学历频次编码(出现频率越高的学历越常见)\n",
    "    edu_freq = features['NATURE_EDU'].value_counts()\n",
    "    features['NATURE_EDU_FREQ'] = features['NATURE_EDU'].map(edu_freq)\n",
    "    features['NATURE_EDU_FREQ_RATIO'] = features['NATURE_EDU_FREQ'] / len(features)\n",
    "    \n",
    "    # 学历频次分层\n",
    "    features['NATURE_EDU_IS_COMMON'] = (features['NATURE_EDU_FREQ_RATIO'] > 0.1).astype(int)  # 常见学历\n",
    "    features['NATURE_EDU_IS_RARE'] = (features['NATURE_EDU_FREQ_RATIO'] < 0.01).astype(int)  # 稀有学历\n",
    "    \n",
    "    # 学历哈希值的数值特征(用于捕获哈希值本身的模式)\n",
    "    # MD5哈希值转换为数值(取前8位十六进制)\n",
    "    def hash_to_numeric(hash_str):\n",
    "        if hash_str == 'UNKNOWN':\n",
    "            return 0\n",
    "        try:\n",
    "            return int(hash_str[:8], 16) if len(hash_str) >= 8 else 0\n",
    "        except:\n",
    "            return 0\n",
    "    \n",
    "    features['NATURE_EDU_HASH_NUM'] = features['NATURE_EDU'].apply(hash_to_numeric)\n",
    "    print(f\"  学历哈希数值范围: {features['NATURE_EDU_HASH_NUM'].min()} ~ {features['NATURE_EDU_HASH_NUM'].max()}\")\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 6. 客户建立时间特征工程 (日期类型处理)\n",
    "    # ============================================================================\n",
    "    print(\"处理客户建立时间特征(日期类型)...\")\n",
    "    \n",
    "    # 保留已转换的日期类型数据\n",
    "    features['NATURE_CUST_CRT_DATE'] = features['IDV_CUST_CRT_TIME']\n",
    "    \n",
    "    # 计算客户年龄(天数) - 使用参考日期2025-05-20\n",
    "    reference_date = pd.to_datetime('20250520')\n",
    "    features['NATURE_CUST_AGE_DAYS'] = (reference_date - features['NATURE_CUST_CRT_DATE']).dt.days\n",
    "    features['NATURE_CUST_AGE_DAYS'] = features['NATURE_CUST_AGE_DAYS'].fillna(0).clip(lower=0)  # 负值处理为0\n",
    "    \n",
    "    # 客户年龄(年)\n",
    "    features['NATURE_CUST_AGE_YEARS'] = features['NATURE_CUST_AGE_DAYS'] / 365.0\n",
    "    \n",
    "    # 客户年龄(月)\n",
    "    features['NATURE_CUST_AGE_MONTHS'] = features['NATURE_CUST_AGE_DAYS'] / 30.0\n",
    "    \n",
    "    # 提取日期组件\n",
    "    features['NATURE_CUST_CRT_YEAR'] = features['NATURE_CUST_CRT_DATE'].dt.year\n",
    "    features['NATURE_CUST_CRT_MONTH'] = features['NATURE_CUST_CRT_DATE'].dt.month\n",
    "    features['NATURE_CUST_CRT_DAY'] = features['NATURE_CUST_CRT_DATE'].dt.day\n",
    "    features['NATURE_CUST_CRT_DAYOFWEEK'] = features['NATURE_CUST_CRT_DATE'].dt.dayofweek\n",
    "    features['NATURE_CUST_CRT_QUARTER'] = features['NATURE_CUST_CRT_DATE'].dt.quarter\n",
    "    \n",
    "    # 是否特殊日期开户\n",
    "    features['NATURE_CUST_IS_MONTH_START'] = (features['NATURE_CUST_CRT_DAY'] <= 5).astype(int)\n",
    "    features['NATURE_CUST_IS_MONTH_END'] = (features['NATURE_CUST_CRT_DAY'] >= 25).astype(int)\n",
    "    features['NATURE_CUST_IS_WEEKEND'] = (features['NATURE_CUST_CRT_DAYOFWEEK'] >= 5).astype(int)\n",
    "    \n",
    "    # 是否老客户/新客户/中等客户\n",
    "    features['NATURE_IS_OLD_CUSTOMER'] = (features['NATURE_CUST_AGE_YEARS'] >= 5).astype(int)\n",
    "    features['NATURE_IS_NEW_CUSTOMER'] = (features['NATURE_CUST_AGE_YEARS'] <= 1).astype(int)\n",
    "    features['NATURE_IS_MID_CUSTOMER'] = ((features['NATURE_CUST_AGE_YEARS'] > 1) & (features['NATURE_CUST_AGE_YEARS'] < 5)).astype(int)\n",
    "    \n",
    "    # 客户年龄分段\n",
    "    features['NATURE_CUST_AGE_GROUP'] = pd.cut(\n",
    "        features['NATURE_CUST_AGE_YEARS'], \n",
    "        bins=[-1, 1, 3, 5, 10, 100], \n",
    "        labels=[1, 2, 3, 4, 5]\n",
    "    )\n",
    "    features['NATURE_CUST_AGE_GROUP'] = features['NATURE_CUST_AGE_GROUP'].cat.add_categories([0]).fillna(0).astype(int)\n",
    "    \n",
    "    # 客户年龄对数变换\n",
    "    features['NATURE_CUST_AGE_LOG'] = np.log1p(features['NATURE_CUST_AGE_DAYS'])\n",
    "    \n",
    "    # 客户年龄平方根变换\n",
    "    features['NATURE_CUST_AGE_SQRT'] = np.sqrt(features['NATURE_CUST_AGE_DAYS'])\n",
    "    \n",
    "    print(f\"  客户年龄范围: {features['NATURE_CUST_AGE_YEARS'].min():.2f} ~ {features['NATURE_CUST_AGE_YEARS'].max():.2f} 年\")\n",
    "    print(f\"  客户建立日期范围: {features['NATURE_CUST_CRT_DATE'].min()} ~ {features['NATURE_CUST_CRT_DATE'].max()}\")\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 7. 客户价值等级特征工程\n",
    "    # ============================================================================\n",
    "    print(\"处理客户价值等级特征...\")\n",
    "    \n",
    "    # 保留已转换的价值等级(已经是数字类型,0表示缺失值)\n",
    "    features['NATURE_VALUE_RANK'] = features['IDV_CUST_VLU_RANK']\n",
    "    \n",
    "    # 价值等级类别数量\n",
    "    value_rank_num_categories = len(value_rank_unique)\n",
    "    print(f\"  客户价值等级类别数: {value_rank_num_categories}\")\n",
    "    \n",
    "    # 根据实际类别生成标识特征\n",
    "    for cat in value_rank_unique:\n",
    "        if cat != 0:  # 排除缺失值标识\n",
    "            features[f'NATURE_IS_VALUE_RANK_{cat}'] = (features['NATURE_VALUE_RANK'] == cat).astype(int)\n",
    "    \n",
    "    # 是否缺失价值等级信息\n",
    "    features['NATURE_VALUE_RANK_MISSING'] = (features['NATURE_VALUE_RANK'] == 0).astype(int)\n",
    "    \n",
    "    # 价值等级分层(假设数值越小价值越高: A=1高价值, B=2中价值, C=3低价值)\n",
    "    valid_values = [v for v in value_rank_unique if v != 0]\n",
    "    if len(valid_values) >= 3:\n",
    "        sorted_value = sorted(valid_values)\n",
    "        high_value_threshold = sorted_value[0]  # 1 (A)\n",
    "        mid_value_threshold = sorted_value[1]   # 2 (B)\n",
    "        low_value_threshold = sorted_value[2]   # 3 (C)\n",
    "        \n",
    "        features['NATURE_IS_HIGH_VALUE'] = (features['NATURE_VALUE_RANK'] == high_value_threshold).astype(int)\n",
    "        features['NATURE_IS_MID_VALUE'] = (features['NATURE_VALUE_RANK'] == mid_value_threshold).astype(int)\n",
    "        features['NATURE_IS_LOW_VALUE'] = (features['NATURE_VALUE_RANK'] == low_value_threshold).astype(int)\n",
    "    elif len(valid_values) == 2:\n",
    "        sorted_value = sorted(valid_values)\n",
    "        features['NATURE_IS_HIGH_VALUE'] = (features['NATURE_VALUE_RANK'] == sorted_value[0]).astype(int)\n",
    "        features['NATURE_IS_LOW_VALUE'] = (features['NATURE_VALUE_RANK'] == sorted_value[1]).astype(int)\n",
    "    \n",
    "    # 价值等级倒数(突出高价值客户)\n",
    "    features['NATURE_VALUE_INVERSE'] = 1.0 / (features['NATURE_VALUE_RANK'] + 1)\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 8. 持卡数量特征工程\n",
    "    # ============================================================================\n",
    "    print(\"处理持卡数量特征...\")\n",
    "    \n",
    "    features['NATURE_DCARD_CNT'] = features['HOLD_DCARD_CNT'].fillna(0)\n",
    "    features['NATURE_CCARD_CNT'] = features['HOLD_CCARD_CNT'].fillna(0)\n",
    "    \n",
    "    # 持卡总数\n",
    "    features['NATURE_TOTAL_CARD_CNT'] = features['NATURE_DCARD_CNT'] + features['NATURE_CCARD_CNT']\n",
    "    \n",
    "    # 持卡状态\n",
    "    features['NATURE_HAS_DCARD'] = (features['NATURE_DCARD_CNT'] > 0).astype(int)\n",
    "    features['NATURE_HAS_CCARD'] = (features['NATURE_CCARD_CNT'] > 0).astype(int)\n",
    "    features['NATURE_HAS_BOTH_CARD'] = ((features['NATURE_DCARD_CNT'] > 0) & (features['NATURE_CCARD_CNT'] > 0)).astype(int)\n",
    "    features['NATURE_HAS_MULTI_DCARD'] = (features['NATURE_DCARD_CNT'] > 1).astype(int)\n",
    "    features['NATURE_HAS_MULTI_CCARD'] = (features['NATURE_CCARD_CNT'] > 1).astype(int)\n",
    "    \n",
    "    # 借记卡信用卡比率\n",
    "    features['NATURE_DCARD_CCARD_RATIO'] = features['NATURE_DCARD_CNT'] / (features['NATURE_CCARD_CNT'] + 1)\n",
    "    features['NATURE_CCARD_DCARD_RATIO'] = features['NATURE_CCARD_CNT'] / (features['NATURE_DCARD_CNT'] + 1)\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 9. 年龄与性别交互特征\n",
    "    # ============================================================================\n",
    "    print(\"生成年龄与性别交互特征...\")\n",
    "    \n",
    "    features['NATURE_AGE_SEX'] = features['AGE'] * features['NATURE_SEX']\n",
    "    features['NATURE_AGE_GROUP_SEX'] = features['NATURE_AGE_GROUP_FINE'] * features['NATURE_SEX']\n",
    "    \n",
    "    # 按性别分组统计年龄\n",
    "    features['NATURE_AGE_BY_SEX_MEAN'] = features.groupby('NATURE_SEX')['AGE'].transform('mean')\n",
    "    features['NATURE_AGE_BY_SEX_STD'] = features.groupby('NATURE_SEX')['AGE'].transform('std')\n",
    "    features['NATURE_AGE_BY_SEX_DIFF'] = features['AGE'] - features['NATURE_AGE_BY_SEX_MEAN']\n",
    "    features['NATURE_AGE_BY_SEX_RATIO'] = features['AGE'] / (features['NATURE_AGE_BY_SEX_MEAN'] + 1e-5)\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 10. 年龄与价值等级交互特征\n",
    "    # ============================================================================\n",
    "    print(\"生成年龄与价值等级交互特征...\")\n",
    "    \n",
    "    features['NATURE_AGE_VALUE'] = features['AGE'] * features['NATURE_VALUE_RANK']\n",
    "    features['NATURE_AGE_GROUP_VALUE'] = features['NATURE_AGE_GROUP_FINE'] * features['NATURE_VALUE_RANK']\n",
    "    \n",
    "    # 按价值等级分组统计年龄\n",
    "    features['NATURE_AGE_BY_VALUE_MEAN'] = features.groupby('NATURE_VALUE_RANK')['AGE'].transform('mean')\n",
    "    features['NATURE_AGE_BY_VALUE_DIFF'] = features['AGE'] - features['NATURE_AGE_BY_VALUE_MEAN']\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 11. 其他交互特征\n",
    "    # ============================================================================\n",
    "    print(\"生成其他交互特征...\")\n",
    "    \n",
    "    # 年龄与婚姻状况交互\n",
    "    features['NATURE_AGE_MARRIAGE'] = features['AGE'] * features['NATURE_MARRIAGE']\n",
    "    \n",
    "    # 年龄与客户年龄交互\n",
    "    features['NATURE_AGE_CUST_AGE'] = features['AGE'] * features['NATURE_CUST_AGE_YEARS']\n",
    "    features['NATURE_AGE_CUST_AGE_RATIO'] = features['AGE'] / (features['NATURE_CUST_AGE_YEARS'] + 1)\n",
    "    \n",
    "    # 性别与价值等级交互\n",
    "    features['NATURE_SEX_VALUE'] = features['NATURE_SEX'] * features['NATURE_VALUE_RANK']\n",
    "    \n",
    "    # 婚姻与价值等级交互\n",
    "    features['NATURE_MARRIAGE_VALUE'] = features['NATURE_MARRIAGE'] * features['NATURE_VALUE_RANK']\n",
    "    \n",
    "    # 持卡数量与价值等级交互\n",
    "    features['NATURE_CARD_VALUE'] = features['NATURE_TOTAL_CARD_CNT'] * features['NATURE_VALUE_RANK']\n",
    "    features['NATURE_CCARD_VALUE'] = features['NATURE_CCARD_CNT'] * features['NATURE_VALUE_RANK']\n",
    "    \n",
    "    # 三阶交互特征\n",
    "    features['NATURE_AGE_SEX_VALUE'] = features['AGE'] * features['NATURE_SEX'] * features['NATURE_VALUE_RANK']\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 12. 客户画像特征\n",
    "    # ============================================================================\n",
    "    print(\"生成客户画像特征...\")\n",
    "    \n",
    "    # 高价值年轻客户\n",
    "    features['NATURE_IS_HIGH_VALUE_YOUNG'] = ((features['NATURE_IS_HIGH_VALUE'] == 1) & (features['NATURE_IS_YOUNG'] == 1)).astype(int)\n",
    "    # 高价值中年客户\n",
    "    features['NATURE_IS_HIGH_VALUE_MIDDLE'] = ((features['NATURE_IS_HIGH_VALUE'] == 1) & (features['NATURE_IS_MIDDLE'] == 1)).astype(int)\n",
    "    # 老客户高价值\n",
    "    features['NATURE_IS_OLD_CUST_HIGH_VALUE'] = ((features['NATURE_IS_OLD_CUSTOMER'] == 1) & (features['NATURE_IS_HIGH_VALUE'] == 1)).astype(int)\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 13. 客户潜力评分\n",
    "    # ============================================================================\n",
    "    print(\"生成客户潜力评分...\")\n",
    "    \n",
    "    features['NATURE_POTENTIAL_SCORE'] = (\n",
    "        features['NATURE_IS_PRIME_AGE'] * 10 +  # 黄金年龄\n",
    "        features['NATURE_IS_MARRIED'] * 5 +  # 已婚\n",
    "        features['NATURE_IS_HIGH_VALUE'] * 15 +  # 高价值\n",
    "        features['NATURE_IS_OLD_CUSTOMER'] * 5 +  # 老客户\n",
    "        features['NATURE_HAS_BOTH_CARD'] * 3  # 双卡客户\n",
    "    )\n",
    "    \n",
    "    # ============================================================================\n",
    "    # 14. 删除原始列和中间列\n",
    "    # ============================================================================\n",
    "    drop_cols = ['DATA_DAT', 'IDV_CUST_SEX', 'IDV_CUST_MRGE_STS', 'IDV_CUST_HEDU', \n",
    "                 'IDV_CUST_CRT_TIME', 'IDV_CUST_VLU_RANK', 'HOLD_DCARD_CNT', 'HOLD_CCARD_CNT', \n",
    "                 'NATURE_CUST_CRT_DATE', 'NATURE_EDU']  # 删除原始列和中间变量\n",
    "    features = features.drop(columns=[col for col in drop_cols if col in features.columns])\n",
    "    \n",
    "    print(f\"\\n处理后特征维度: {features.shape}\")\n",
    "    print(f\"新增特征数: {features.shape[1] - 2}\")  # 减去CUST_NO和AGE\n",
    "    \n",
    "    return features\n",
    "\n",
    "# 处理训练集和测试集\n",
    "print(\"\\n处理训练集:\")\n",
    "TRAIN_NATURE_features = process_nature_features(TRAIN_NATURE_features)\n",
    "\n",
    "print(\"\\n\" + \"=\"*100)\n",
    "\n",
    "print(\"\\n处理测试集:\")\n",
    "A_NATURE_features = process_nature_features(A_NATURE_features)\n",
    "\n",
    "print(\"\\n\" + \"=\"*100)\n",
    "print(\"自然属性信息表特征工程完成!\")\n",
    "print(\"=\"*100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba131e22",
   "metadata": {},
   "source": [
    "## 2. 资产负债表特征工程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "918a29a2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "====================================================================================================\n",
      "开始处理资产负债表 (ASSET)\n",
      "====================================================================================================\n",
      "原始数据维度: (1000, 34)\n",
      "执行金额立方根变换...\n",
      "计算基础资产指标...\n",
      "计算衍生资产指标...\n",
      "计算资产比率特征...\n",
      "计算时间差值特征...\n",
      "计算增长率特征...\n",
      "计算波动性特征...\n",
      "计算趋势特征...\n",
      "计算分位数特征...\n",
      "计算状态标识特征...\n",
      "计算非线性变换特征...\n",
      "处理后特征维度: (1000, 178)\n",
      "新增特征数: 177\n",
      "\n",
      "\n",
      "原始数据维度: (500, 34)\n",
      "执行金额立方根变换...\n",
      "计算基础资产指标...\n",
      "计算衍生资产指标...\n",
      "计算资产比率特征...\n",
      "计算时间差值特征...\n",
      "计算增长率特征...\n",
      "计算波动性特征...\n",
      "计算趋势特征...\n",
      "计算分位数特征...\n",
      "计算状态标识特征...\n",
      "计算非线性变换特征...\n",
      "处理后特征维度: (500, 178)\n",
      "新增特征数: 177\n",
      "\n",
      "====================================================================================================\n",
      "资产负债表特征工程完成!\n",
      "====================================================================================================\n"
     ]
    }
   ],
   "source": [
    "print(\"=\"*100)\n",
    "print(\"开始处理资产负债表 (ASSET)\")\n",
    "print(\"=\"*100)\n",
    "\n",
    "# 训练集\n",
    "TRAIN_ASSET_features = TRAIN_ASSET_data.copy()\n",
    "# 测试集\n",
    "A_ASSET_features = A_ASSET_data.copy()\n",
    "\n",
    "def process_asset_features(df):\n",
    "    \"\"\"\n",
    "    资产负债表特征工程\n",
    "    包含: 金融资产、AUM、存款、活期存款、贷款、理财、基金、保险等\n",
    "    \"\"\"\n",
    "    print(f\"原始数据维度: {df.shape}\")\n",
    "    \n",
    "    features = df.copy()\n",
    "    \n",
    "    # 填充缺失值\n",
    "    for col in features.columns:\n",
    "        if col not in ['CUST_NO', 'DATA_DAT']:\n",
    "            features[col] = features[col].fillna(0)\n",
    "    \n",
    "    # 1. 金额立方根变换 - 减少极值影响(参考往年赛题经验)\n",
    "    print(\"执行金额立方根变换...\")\n",
    "    transform_cols = [col for col in features.columns if col not in ['CUST_NO', 'DATA_DAT']]\n",
    "    for col in transform_cols:\n",
    "        features[col] = features[col].apply(lambda x: round(pow(x/3.12, 3), 2) if x > 0 else 0)\n",
    "    \n",
    "    # 2. 基础资产计算\n",
    "    print(\"计算基础资产指标...\")\n",
    "    \n",
    "    # 2.1 各时期金融资产 = 存款 + 理财 + 基金 + 保险\n",
    "    features['ASSET_DAY_FA'] = features['FA_BAL']\n",
    "    features['ASSET_MAVER_FA'] = features['FA_MAVER_BAL']\n",
    "    features['ASSET_3M_FA'] = features['FA_BAL_LST_3_MTH']\n",
    "    features['ASSET_6M_FA'] = features['FA_BAL_LST_6_MTH']\n",
    "    features['ASSET_12M_FA'] = features['FA_BAL_LST_12_MTH']\n",
    "    \n",
    "    # 2.2 各时期AUM\n",
    "    features['ASSET_DAY_AUM'] = features['AUM_BAL']\n",
    "    features['ASSET_MAVER_AUM'] = features['AUM_MAVER_BAL']\n",
    "    features['ASSET_3M_AUM'] = features['AUM_BAL_LST_3_MTH']\n",
    "    features['ASSET_6M_AUM'] = features['AUM_BAL_LST_6_MTH']\n",
    "    features['ASSET_12M_AUM'] = features['AUM_BAL_LST_12_MTH']\n",
    "    features['ASSET_MAX_AUM'] = features['AUM_BAL_MAX']\n",
    "    \n",
    "    # 2.3 各时期存款\n",
    "    features['ASSET_DAY_DP'] = features['DP_BAL']\n",
    "    features['ASSET_3M_DP'] = features['DP_BAL_LST_3_MTH']\n",
    "    features['ASSET_6M_DP'] = features['DP_BAL_LST_6_MTH']\n",
    "    features['ASSET_12M_DP'] = features['DP_BAL_LST_12_MTH']\n",
    "    \n",
    "    # 2.4 各时期活期存款\n",
    "    features['ASSET_DAY_DPSA'] = features['DPSA_BAL']\n",
    "    features['ASSET_3M_DPSA'] = features['DPSA_BAL_LST_3_MTH']\n",
    "    features['ASSET_6M_DPSA'] = features['DPSA_BAL_LST_6_MTH']\n",
    "    features['ASSET_12M_DPSA'] = features['DPSA_BAL_LST_12_MTH']\n",
    "    \n",
    "    # 2.5 各时期贷款\n",
    "    features['ASSET_DAY_LOAN'] = features['LOAN_BAL']\n",
    "    features['ASSET_MAVER_LOAN'] = features['LOAN_MAVER_BAL']\n",
    "    features['ASSET_3M_LOAN'] = features['LOAN_BAL_LST_3_MTH']\n",
    "    features['ASSET_6M_LOAN'] = features['LOAN_BAL_LST_6_MTH']\n",
    "    \n",
    "    # 2.6 理财\n",
    "    features['ASSET_DAY_FNCG'] = features['FNCG_BAL']\n",
    "    features['ASSET_MAVER_FNCG'] = features['FNCG_MAVER_BAL']\n",
    "    features['ASSET_YAVER_FNCG'] = features['FNCG_YAVER_BAL']\n",
    "    \n",
    "    # 2.7 基金\n",
    "    features['ASSET_DAY_FUND'] = features['FUND_BAL']\n",
    "    features['ASSET_MAVER_FUND'] = features['FUND_MAVER_BAL']\n",
    "    features['ASSET_YAVER_FUND'] = features['FUND_YAVER_BAL']\n",
    "    \n",
    "    # 2.8 保险\n",
    "    features['ASSET_DAY_INSUR'] = features['INSUR_BAL']\n",
    "    features['ASSET_MAVER_INSUR'] = features['INSUR_MAVER_BAL']\n",
    "    features['ASSET_YAVER_INSUR'] = features['INSUR_YAVER_BAL']\n",
    "    \n",
    "    # 3. 衍生资产计算\n",
    "    print(\"计算衍生资产指标...\")\n",
    "    \n",
    "    # 3.1 定期存款 = 总存款 - 活期存款\n",
    "    features['ASSET_DAY_TD'] = features['ASSET_DAY_DP'] - features['ASSET_DAY_DPSA']\n",
    "    features['ASSET_3M_TD'] = features['ASSET_3M_DP'] - features['ASSET_3M_DPSA']\n",
    "    features['ASSET_6M_TD'] = features['ASSET_6M_DP'] - features['ASSET_6M_DPSA']\n",
    "    features['ASSET_12M_TD'] = features['ASSET_12M_DP'] - features['ASSET_12M_DPSA']\n",
    "    \n",
    "    # 3.2 其他资产 = AUM - 存款\n",
    "    features['ASSET_DAY_OTHER'] = features['ASSET_DAY_AUM'] - features['ASSET_DAY_DP']\n",
    "    features['ASSET_MAVER_OTHER'] = features['ASSET_MAVER_AUM'] - features['ASSET_DAY_DP']\n",
    "    features['ASSET_3M_OTHER'] = features['ASSET_3M_AUM'] - features['ASSET_3M_DP']\n",
    "    features['ASSET_6M_OTHER'] = features['ASSET_6M_AUM'] - features['ASSET_6M_DP']\n",
    "    features['ASSET_12M_OTHER'] = features['ASSET_12M_AUM'] - features['ASSET_12M_DP']\n",
    "    \n",
    "    # 3.3 投资类资产 = 理财 + 基金 + 保险\n",
    "    features['ASSET_DAY_INVEST'] = features['ASSET_DAY_FNCG'] + features['ASSET_DAY_FUND'] + features['ASSET_DAY_INSUR']\n",
    "    features['ASSET_MAVER_INVEST'] = features['ASSET_MAVER_FNCG'] + features['ASSET_MAVER_FUND'] + features['ASSET_MAVER_INSUR']\n",
    "    features['ASSET_YAVER_INVEST'] = features['ASSET_YAVER_FNCG'] + features['ASSET_YAVER_FUND'] + features['ASSET_YAVER_INSUR']\n",
    "    \n",
    "    # 4. 比率特征\n",
    "    print(\"计算资产比率特征...\")\n",
    "    \n",
    "    # 4.1 AUM相关比率\n",
    "    features['ASSET_AUM_FA_RATIO'] = features['ASSET_DAY_AUM'] / (features['ASSET_DAY_FA'] + 1e-5)\n",
    "    features['ASSET_DP_AUM_RATIO'] = features['ASSET_DAY_DP'] / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    features['ASSET_DPSA_AUM_RATIO'] = features['ASSET_DAY_DPSA'] / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    features['ASSET_TD_AUM_RATIO'] = features['ASSET_DAY_TD'] / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    features['ASSET_OTHER_AUM_RATIO'] = features['ASSET_DAY_OTHER'] / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    features['ASSET_INVEST_AUM_RATIO'] = features['ASSET_DAY_INVEST'] / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    \n",
    "    # 4.2 存款结构比率\n",
    "    features['ASSET_DPSA_DP_RATIO'] = features['ASSET_DAY_DPSA'] / (features['ASSET_DAY_DP'] + 1e-5)\n",
    "    features['ASSET_TD_DP_RATIO'] = features['ASSET_DAY_TD'] / (features['ASSET_DAY_DP'] + 1e-5)\n",
    "    features['ASSET_DPSA_TD_RATIO'] = features['ASSET_DAY_DPSA'] / (features['ASSET_DAY_TD'] + 1e-5)\n",
    "    \n",
    "    # 4.3 存贷比\n",
    "    features['ASSET_DP_LOAN_RATIO'] = features['ASSET_DAY_DP'] / (features['ASSET_DAY_LOAN'] + 1e-5)\n",
    "    features['ASSET_DPSA_LOAN_RATIO'] = features['ASSET_DAY_DPSA'] / (features['ASSET_DAY_LOAN'] + 1e-5)\n",
    "    features['ASSET_TD_LOAN_RATIO'] = features['ASSET_DAY_TD'] / (features['ASSET_DAY_LOAN'] + 1e-5)\n",
    "    features['ASSET_AUM_LOAN_RATIO'] = features['ASSET_DAY_AUM'] / (features['ASSET_DAY_LOAN'] + 1e-5)\n",
    "    features['ASSET_LOAN_AUM_RATIO'] = features['ASSET_DAY_LOAN'] / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    \n",
    "    # 4.4 投资产品比率\n",
    "    features['ASSET_FNCG_INVEST_RATIO'] = features['ASSET_DAY_FNCG'] / (features['ASSET_DAY_INVEST'] + 1e-5)\n",
    "    features['ASSET_FUND_INVEST_RATIO'] = features['ASSET_DAY_FUND'] / (features['ASSET_DAY_INVEST'] + 1e-5)\n",
    "    features['ASSET_INSUR_INVEST_RATIO'] = features['ASSET_DAY_INSUR'] / (features['ASSET_DAY_INVEST'] + 1e-5)\n",
    "    \n",
    "    features['ASSET_FNCG_AUM_RATIO'] = features['ASSET_DAY_FNCG'] / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    features['ASSET_FUND_AUM_RATIO'] = features['ASSET_DAY_FUND'] / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    features['ASSET_INSUR_AUM_RATIO'] = features['ASSET_DAY_INSUR'] / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    \n",
    "    # 5. 差值特征 - 时间维度\n",
    "    print(\"计算时间差值特征...\")\n",
    "    \n",
    "    # 5.1 AUM差值\n",
    "    features['ASSET_AUM_DIFF_DAY_MAVER'] = features['ASSET_MAVER_AUM'] - features['ASSET_DAY_AUM']\n",
    "    features['ASSET_AUM_DIFF_DAY_3M'] = features['ASSET_3M_AUM'] - features['ASSET_DAY_AUM']\n",
    "    features['ASSET_AUM_DIFF_DAY_6M'] = features['ASSET_6M_AUM'] - features['ASSET_DAY_AUM']\n",
    "    features['ASSET_AUM_DIFF_DAY_12M'] = features['ASSET_12M_AUM'] - features['ASSET_DAY_AUM']\n",
    "    features['ASSET_AUM_DIFF_MAVER_3M'] = features['ASSET_3M_AUM'] - features['ASSET_MAVER_AUM']\n",
    "    features['ASSET_AUM_DIFF_3M_6M'] = features['ASSET_6M_AUM'] - features['ASSET_3M_AUM']\n",
    "    features['ASSET_AUM_DIFF_6M_12M'] = features['ASSET_12M_AUM'] - features['ASSET_6M_AUM']\n",
    "    features['ASSET_AUM_DIFF_DAY_MAX'] = features['ASSET_MAX_AUM'] - features['ASSET_DAY_AUM']\n",
    "    features['ASSET_AUM_DIFF_MAVER_MAX'] = features['ASSET_MAX_AUM'] - features['ASSET_MAVER_AUM']\n",
    "    features['ASSET_AUM_DIFF_3M_MAX'] = features['ASSET_MAX_AUM'] - features['ASSET_3M_AUM']\n",
    "    \n",
    "    # 5.2 存款差值\n",
    "    features['ASSET_DP_DIFF_DAY_3M'] = features['ASSET_3M_DP'] - features['ASSET_DAY_DP']\n",
    "    features['ASSET_DP_DIFF_DAY_6M'] = features['ASSET_6M_DP'] - features['ASSET_DAY_DP']\n",
    "    features['ASSET_DP_DIFF_DAY_12M'] = features['ASSET_12M_DP'] - features['ASSET_DAY_DP']\n",
    "    features['ASSET_DP_DIFF_3M_6M'] = features['ASSET_6M_DP'] - features['ASSET_3M_DP']\n",
    "    features['ASSET_DP_DIFF_6M_12M'] = features['ASSET_12M_DP'] - features['ASSET_6M_DP']\n",
    "    \n",
    "    # 5.3 活期存款差值\n",
    "    features['ASSET_DPSA_DIFF_DAY_3M'] = features['ASSET_3M_DPSA'] - features['ASSET_DAY_DPSA']\n",
    "    features['ASSET_DPSA_DIFF_DAY_6M'] = features['ASSET_6M_DPSA'] - features['ASSET_DAY_DPSA']\n",
    "    features['ASSET_DPSA_DIFF_DAY_12M'] = features['ASSET_12M_DPSA'] - features['ASSET_DAY_DPSA']\n",
    "    features['ASSET_DPSA_DIFF_3M_6M'] = features['ASSET_6M_DPSA'] - features['ASSET_3M_DPSA']\n",
    "    features['ASSET_DPSA_DIFF_6M_12M'] = features['ASSET_12M_DPSA'] - features['ASSET_6M_DPSA']\n",
    "    \n",
    "    # 5.4 定期存款差值\n",
    "    features['ASSET_TD_DIFF_DAY_3M'] = features['ASSET_3M_TD'] - features['ASSET_DAY_TD']\n",
    "    features['ASSET_TD_DIFF_DAY_6M'] = features['ASSET_6M_TD'] - features['ASSET_DAY_TD']\n",
    "    features['ASSET_TD_DIFF_DAY_12M'] = features['ASSET_12M_TD'] - features['ASSET_DAY_TD']\n",
    "    \n",
    "    # 5.5 贷款差值\n",
    "    features['ASSET_LOAN_DIFF_DAY_MAVER'] = features['ASSET_MAVER_LOAN'] - features['ASSET_DAY_LOAN']\n",
    "    features['ASSET_LOAN_DIFF_DAY_3M'] = features['ASSET_3M_LOAN'] - features['ASSET_DAY_LOAN']\n",
    "    features['ASSET_LOAN_DIFF_DAY_6M'] = features['ASSET_6M_LOAN'] - features['ASSET_DAY_LOAN']\n",
    "    features['ASSET_LOAN_DIFF_MAVER_3M'] = features['ASSET_3M_LOAN'] - features['ASSET_MAVER_LOAN']\n",
    "    features['ASSET_LOAN_DIFF_3M_6M'] = features['ASSET_6M_LOAN'] - features['ASSET_3M_LOAN']\n",
    "    \n",
    "    # 5.6 金融资产差值\n",
    "    features['ASSET_FA_DIFF_DAY_MAVER'] = features['ASSET_MAVER_FA'] - features['ASSET_DAY_FA']\n",
    "    features['ASSET_FA_DIFF_DAY_3M'] = features['ASSET_3M_FA'] - features['ASSET_DAY_FA']\n",
    "    features['ASSET_FA_DIFF_DAY_6M'] = features['ASSET_6M_FA'] - features['ASSET_DAY_FA']\n",
    "    features['ASSET_FA_DIFF_DAY_12M'] = features['ASSET_12M_FA'] - features['ASSET_DAY_FA']\n",
    "    \n",
    "    # 6. 增长率特征\n",
    "    print(\"计算增长率特征...\")\n",
    "    \n",
    "    # 6.1 AUM增长率\n",
    "    features['ASSET_AUM_GROWTH_DAY_MAVER'] = (features['ASSET_MAVER_AUM'] - features['ASSET_DAY_AUM']) / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    features['ASSET_AUM_GROWTH_DAY_3M'] = (features['ASSET_3M_AUM'] - features['ASSET_DAY_AUM']) / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    features['ASSET_AUM_GROWTH_DAY_6M'] = (features['ASSET_6M_AUM'] - features['ASSET_DAY_AUM']) / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    features['ASSET_AUM_GROWTH_DAY_12M'] = (features['ASSET_12M_AUM'] - features['ASSET_DAY_AUM']) / (features['ASSET_DAY_AUM'] + 1e-5)\n",
    "    features['ASSET_AUM_GROWTH_3M_6M'] = (features['ASSET_6M_AUM'] - features['ASSET_3M_AUM']) / (features['ASSET_3M_AUM'] + 1e-5)\n",
    "    features['ASSET_AUM_GROWTH_6M_12M'] = (features['ASSET_12M_AUM'] - features['ASSET_6M_AUM']) / (features['ASSET_6M_AUM'] + 1e-5)\n",
    "    \n",
    "    # 6.2 存款增长率\n",
    "    features['ASSET_DP_GROWTH_DAY_3M'] = (features['ASSET_3M_DP'] - features['ASSET_DAY_DP']) / (features['ASSET_DAY_DP'] + 1e-5)\n",
    "    features['ASSET_DP_GROWTH_DAY_6M'] = (features['ASSET_6M_DP'] - features['ASSET_DAY_DP']) / (features['ASSET_DAY_DP'] + 1e-5)\n",
    "    features['ASSET_DP_GROWTH_DAY_12M'] = (features['ASSET_12M_DP'] - features['ASSET_DAY_DP']) / (features['ASSET_DAY_DP'] + 1e-5)\n",
    "    features['ASSET_DP_GROWTH_3M_6M'] = (features['ASSET_6M_DP'] - features['ASSET_3M_DP']) / (features['ASSET_3M_DP'] + 1e-5)\n",
    "    features['ASSET_DP_GROWTH_6M_12M'] = (features['ASSET_12M_DP'] - features['ASSET_6M_DP']) / (features['ASSET_6M_DP'] + 1e-5)\n",
    "    \n",
    "    # 6.3 贷款增长率\n",
    "    features['ASSET_LOAN_GROWTH_DAY_MAVER'] = (features['ASSET_MAVER_LOAN'] - features['ASSET_DAY_LOAN']) / (features['ASSET_DAY_LOAN'] + 1e-5)\n",
    "    features['ASSET_LOAN_GROWTH_DAY_3M'] = (features['ASSET_3M_LOAN'] - features['ASSET_DAY_LOAN']) / (features['ASSET_DAY_LOAN'] + 1e-5)\n",
    "    features['ASSET_LOAN_GROWTH_DAY_6M'] = (features['ASSET_6M_LOAN'] - features['ASSET_DAY_LOAN']) / (features['ASSET_DAY_LOAN'] + 1e-5)\n",
    "    \n",
    "    # 7. 波动性特征\n",
    "    print(\"计算波动性特征...\")\n",
    "    \n",
    "    # 7.1 AUM波动系数\n",
    "    aum_cols = ['ASSET_DAY_AUM', 'ASSET_MAVER_AUM', 'ASSET_3M_AUM', 'ASSET_6M_AUM', 'ASSET_12M_AUM']\n",
    "    features['ASSET_AUM_STD'] = features[aum_cols].std(axis=1)\n",
    "    features['ASSET_AUM_MEAN'] = features[aum_cols].mean(axis=1)\n",
    "    features['ASSET_AUM_CV'] = features['ASSET_AUM_STD'] / (features['ASSET_AUM_MEAN'] + 1e-5)\n",
    "    features['ASSET_AUM_MAX_MIN_RATIO'] = features[aum_cols].max(axis=1) / (features[aum_cols].min(axis=1) + 1e-5)\n",
    "    \n",
    "    # 7.2 存款波动系数\n",
    "    dp_cols = ['ASSET_DAY_DP', 'ASSET_3M_DP', 'ASSET_6M_DP', 'ASSET_12M_DP']\n",
    "    features['ASSET_DP_STD'] = features[dp_cols].std(axis=1)\n",
    "    features['ASSET_DP_MEAN'] = features[dp_cols].mean(axis=1)\n",
    "    features['ASSET_DP_CV'] = features['ASSET_DP_STD'] / (features['ASSET_DP_MEAN'] + 1e-5)\n",
    "    features['ASSET_DP_MAX_MIN_RATIO'] = features[dp_cols].max(axis=1) / (features[dp_cols].min(axis=1) + 1e-5)\n",
    "    \n",
    "    # 7.3 贷款波动系数\n",
    "    loan_cols = ['ASSET_DAY_LOAN', 'ASSET_MAVER_LOAN', 'ASSET_3M_LOAN', 'ASSET_6M_LOAN']\n",
    "    features['ASSET_LOAN_STD'] = features[loan_cols].std(axis=1)\n",
    "    features['ASSET_LOAN_MEAN'] = features[loan_cols].mean(axis=1)\n",
    "    features['ASSET_LOAN_CV'] = features['ASSET_LOAN_STD'] / (features['ASSET_LOAN_MEAN'] + 1e-5)\n",
    "    \n",
    "    # 8. 趋势特征\n",
    "    print(\"计算趋势特征...\")\n",
    "    \n",
    "    # 8.1 AUM趋势\n",
    "    features['ASSET_AUM_TREND_UP'] = ((features['ASSET_MAVER_AUM'] > features['ASSET_DAY_AUM']) & \n",
    "                                       (features['ASSET_3M_AUM'] > features['ASSET_MAVER_AUM']) &\n",
    "                                       (features['ASSET_6M_AUM'] > features['ASSET_3M_AUM'])).astype(int)\n",
    "    features['ASSET_AUM_TREND_DOWN'] = ((features['ASSET_MAVER_AUM'] < features['ASSET_DAY_AUM']) & \n",
    "                                         (features['ASSET_3M_AUM'] < features['ASSET_MAVER_AUM']) &\n",
    "                                         (features['ASSET_6M_AUM'] < features['ASSET_3M_AUM'])).astype(int)\n",
    "    \n",
    "    # 8.2 存款趋势\n",
    "    features['ASSET_DP_TREND_UP'] = ((features['ASSET_3M_DP'] > features['ASSET_DAY_DP']) & \n",
    "                                      (features['ASSET_6M_DP'] > features['ASSET_3M_DP']) &\n",
    "                                      (features['ASSET_12M_DP'] > features['ASSET_6M_DP'])).astype(int)\n",
    "    features['ASSET_DP_TREND_DOWN'] = ((features['ASSET_3M_DP'] < features['ASSET_DAY_DP']) & \n",
    "                                        (features['ASSET_6M_DP'] < features['ASSET_3M_DP']) &\n",
    "                                        (features['ASSET_12M_DP'] < features['ASSET_6M_DP'])).astype(int)\n",
    "    \n",
    "    # 9. 分位数特征\n",
    "    print(\"计算分位数特征...\")\n",
    "    \n",
    "    features['ASSET_AUM_RANK'] = features['ASSET_DAY_AUM'].rank(pct=True)\n",
    "    features['ASSET_DP_RANK'] = features['ASSET_DAY_DP'].rank(pct=True)\n",
    "    features['ASSET_LOAN_RANK'] = features['ASSET_DAY_LOAN'].rank(pct=True)\n",
    "    features['ASSET_INVEST_RANK'] = features['ASSET_DAY_INVEST'].rank(pct=True)\n",
    "    \n",
    "    # 10. 状态标识特征\n",
    "    print(\"计算状态标识特征...\")\n",
    "    \n",
    "    # AUM水平\n",
    "    features['ASSET_IS_HIGH_AUM'] = (features['ASSET_AUM_RANK'] >= 0.75).astype(int)\n",
    "    features['ASSET_IS_MID_AUM'] = ((features['ASSET_AUM_RANK'] >= 0.25) & (features['ASSET_AUM_RANK'] < 0.75)).astype(int)\n",
    "    features['ASSET_IS_LOW_AUM'] = (features['ASSET_AUM_RANK'] < 0.25).astype(int)\n",
    "    \n",
    "    # 贷款状态\n",
    "    features['ASSET_HAS_LOAN'] = (features['ASSET_DAY_LOAN'] > 0).astype(int)\n",
    "    features['ASSET_IS_HIGH_LOAN'] = (features['ASSET_LOAN_RANK'] >= 0.75).astype(int)\n",
    "    \n",
    "    # 投资状态\n",
    "    features['ASSET_HAS_INVEST'] = (features['ASSET_DAY_INVEST'] > 0).astype(int)\n",
    "    features['ASSET_HAS_FNCG'] = (features['ASSET_DAY_FNCG'] > 0).astype(int)\n",
    "    features['ASSET_HAS_FUND'] = (features['ASSET_DAY_FUND'] > 0).astype(int)\n",
    "    features['ASSET_HAS_INSUR'] = (features['ASSET_DAY_INSUR'] > 0).astype(int)\n",
    "    \n",
    "    # 投资多样化\n",
    "    features['ASSET_INVEST_DIVERSITY'] = (features['ASSET_HAS_FNCG'] + features['ASSET_HAS_FUND'] + features['ASSET_HAS_INSUR'])\n",
    "    features['ASSET_IS_DIVERSIFIED'] = (features['ASSET_INVEST_DIVERSITY'] >= 2).astype(int)\n",
    "    \n",
    "    # 11. 非线性变换\n",
    "    print(\"计算非线性变换特征...\")\n",
    "    \n",
    "    # 对数变换\n",
    "    features['ASSET_AUM_LOG'] = np.log1p(features['ASSET_DAY_AUM'])\n",
    "    features['ASSET_DP_LOG'] = np.log1p(features['ASSET_DAY_DP'])\n",
    "    features['ASSET_LOAN_LOG'] = np.log1p(features['ASSET_DAY_LOAN'])\n",
    "    \n",
    "    # 平方根变换\n",
    "    features['ASSET_AUM_SQRT'] = np.sqrt(features['ASSET_DAY_AUM'])\n",
    "    features['ASSET_DP_SQRT'] = np.sqrt(features['ASSET_DAY_DP'])\n",
    "    \n",
    "    # 删除原始列\n",
    "    drop_cols = ['DATA_DAT']\n",
    "    features = features.drop(columns=[col for col in drop_cols if col in features.columns])\n",
    "    \n",
    "    print(f\"处理后特征维度: {features.shape}\")\n",
    "    print(f\"新增特征数: {features.shape[1] - 1}\")  # 减去CUST_NO\n",
    "    \n",
    "    return features\n",
    "\n",
    "# 处理训练集和测试集\n",
    "TRAIN_ASSET_features = process_asset_features(TRAIN_ASSET_features)\n",
    "print(\"\\n\")\n",
    "A_ASSET_features = process_asset_features(A_ASSET_features)\n",
    "\n",
    "print(\"\\n\" + \"=\"*100)\n",
    "print(\"资产负债表特征工程完成!\")\n",
    "print(\"=\"*100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ab267f4",
   "metadata": {},
   "source": [
    "## 3. 产品持有信息表特征工程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "7e98905c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "====================================================================================================\n",
      "开始处理产品持有信息表 (PROD_HOLD)\n",
      "====================================================================================================\n",
      "原始数据维度: (1000, 15)\n",
      "处理基础产品持有情况...\n",
      "统计产品类别...\n",
      "生成产品组合特征...\n",
      "计算产品价值评分...\n",
      "计算产品活跃度...\n",
      "计算产品覆盖率...\n",
      "生成客户分层特征...\n",
      "生成客户类型标识...\n",
      "计算产品多样性...\n",
      "计算产品比率特征...\n",
      "生成产品交互特征...\n",
      "计算客户潜力评分...\n",
      "处理后特征维度: (1000, 75)\n",
      "新增特征数: 74\n",
      "\n",
      "\n",
      "原始数据维度: (500, 15)\n",
      "处理基础产品持有情况...\n",
      "统计产品类别...\n",
      "生成产品组合特征...\n",
      "计算产品价值评分...\n",
      "计算产品活跃度...\n",
      "计算产品覆盖率...\n",
      "生成客户分层特征...\n",
      "生成客户类型标识...\n",
      "计算产品多样性...\n",
      "计算产品比率特征...\n",
      "生成产品交互特征...\n",
      "计算客户潜力评分...\n",
      "处理后特征维度: (500, 75)\n",
      "新增特征数: 74\n",
      "\n",
      "====================================================================================================\n",
      "产品持有信息表特征工程完成!\n",
      "====================================================================================================\n"
     ]
    }
   ],
   "source": [
    "print(\"=\"*100)\n",
    "print(\"开始处理产品持有信息表 (PROD_HOLD)\")\n",
    "print(\"=\"*100)\n",
    "\n",
    "# 训练集\n",
    "TRAIN_PROD_HOLD_features = TRAIN_PROD_HOLD_data.copy()\n",
    "# 测试集\n",
    "A_PROD_HOLD_features = A_PROD_HOLD_data.copy()\n",
    "\n",
    "def process_prod_hold_features(df):\n",
    "    \"\"\"\n",
    "    产品持有信息表特征工程\n",
    "    包含: 存款、贷款、借记卡、贷记卡、理财、基金、国债、保险、贵金属、三方支付等\n",
    "    \"\"\"\n",
    "    print(f\"原始数据维度: {df.shape}\")\n",
    "    \n",
    "    features = df.copy()\n",
    "    \n",
    "    # 填充缺失值并转换为数值\n",
    "    for col in features.columns:\n",
    "        if col not in ['CUST_NO', 'DATA_DAT']:\n",
    "            features[col] = features[col].fillna(0).astype(int)\n",
    "    \n",
    "    # 1. 基础产品持有情况\n",
    "    print(\"处理基础产品持有情况...\")\n",
    "    \n",
    "    # 获取所有产品标识列\n",
    "    product_cols = [col for col in features.columns if col.endswith('_IND') and col not in ['CUST_NO', 'DATA_DAT']]\n",
    "    \n",
    "    # 产品持有总数\n",
    "    features['PROD_TOTAL_CNT'] = features[product_cols].sum(axis=1)\n",
    "    \n",
    "    # 2. 产品类别统计\n",
    "    print(\"统计产品类别...\")\n",
    "    \n",
    "    # 2.1 存款和贷款\n",
    "    features['PROD_HAS_DP'] = features['DP_IND']\n",
    "    features['PROD_HAS_LOAN'] = features['LOAN_IND']\n",
    "    \n",
    "    # 2.2 卡类产品\n",
    "    features['PROD_HAS_DCARD'] = features['DCARD_IND']\n",
    "    features['PROD_HAS_CCARD'] = features['CCARD_IND']\n",
    "    features['PROD_CARD_CNT'] = features['DCARD_IND'] + features['CCARD_IND']\n",
    "    features['PROD_HAS_BOTH_CARD'] = ((features['DCARD_IND'] == 1) & (features['CCARD_IND'] == 1)).astype(int)\n",
    "    features['PROD_ONLY_DCARD'] = ((features['DCARD_IND'] == 1) & (features['CCARD_IND'] == 0)).astype(int)\n",
    "    features['PROD_ONLY_CCARD'] = ((features['DCARD_IND'] == 0) & (features['CCARD_IND'] == 1)).astype(int)\n",
    "    \n",
    "    # 2.3 理财投资类产品\n",
    "    features['PROD_HAS_FNCG'] = features['FNCG_IND']\n",
    "    features['PROD_HAS_FUND'] = features['FUND_IND']\n",
    "    features['PROD_HAS_BOND'] = features['BOND_IND']\n",
    "    features['PROD_HAS_INSUR'] = features['INSUR_IND']\n",
    "    features['PROD_HAS_GOLD'] = features['METAL_IND']\n",
    "    \n",
    "    features['PROD_WEALTH_CNT'] = (features['FNCG_IND'] + features['FUND_IND'] + \n",
    "                                    features['BOND_IND'] + features['INSUR_IND'] + features['METAL_IND'])\n",
    "    \n",
    "    # 2.4 第三方支付产品\n",
    "    features['PROD_HAS_TPAY_DCARD'] = features['TPAY_DCARD_IND']\n",
    "    features['PROD_HAS_TPAY_CCARD'] = features['TPAY_CCARD_IND']\n",
    "    features['PROD_HAS_TPAY_WX'] = features['TPAY_WX_IND']\n",
    "    features['PROD_HAS_TPAY_ALI'] = features['TPAY_ALI_IND']\n",
    "    \n",
    "    features['PROD_TPAY_CNT'] = (features['TPAY_DCARD_IND'] + features['TPAY_CCARD_IND'] + \n",
    "                                  features['TPAY_WX_IND'] + features['TPAY_ALI_IND'])\n",
    "    \n",
    "    # 3. 产品组合特征\n",
    "    print(\"生成产品组合特征...\")\n",
    "    \n",
    "    # 3.1 基础组合\n",
    "    features['PROD_HAS_LOAN_AND_DP'] = ((features['LOAN_IND'] == 1) & (features['DP_IND'] == 1)).astype(int)\n",
    "    features['PROD_HAS_LOAN_NO_DP'] = ((features['LOAN_IND'] == 1) & (features['DP_IND'] == 0)).astype(int)\n",
    "    \n",
    "    # 3.2 理财组合\n",
    "    features['PROD_HAS_FNCG_AND_FUND'] = ((features['FNCG_IND'] == 1) & (features['FUND_IND'] == 1)).astype(int)\n",
    "    features['PROD_HAS_ALL_WEALTH'] = ((features['FNCG_IND'] == 1) & (features['FUND_IND'] == 1) & \n",
    "                                        (features['INSUR_IND'] == 1)).astype(int)\n",
    "    \n",
    "    # 3.3 第三方支付组合\n",
    "    features['PROD_HAS_BOTH_TPAY'] = ((features['TPAY_WX_IND'] == 1) & (features['TPAY_ALI_IND'] == 1)).astype(int)\n",
    "    features['PROD_HAS_TPAY_BOTH_CARD'] = ((features['TPAY_DCARD_IND'] == 1) & (features['TPAY_CCARD_IND'] == 1)).astype(int)\n",
    "    \n",
    "    # 3.4 完整金融产品组合\n",
    "    features['PROD_FULL_FINANCE'] = ((features['DP_IND'] == 1) & (features['LOAN_IND'] == 1) & \n",
    "                                      (features['FNCG_IND'] == 1) & (features['FUND_IND'] == 1)).astype(int)\n",
    "    \n",
    "    # 4. 产品价值评分\n",
    "    print(\"计算产品价值评分...\")\n",
    "    \n",
    "    # 根据产品重要性赋予不同权重\n",
    "    features['PROD_VALUE_SCORE'] = (\n",
    "        features['LOAN_IND'] * 15 +        # 贷款 - 最高价值\n",
    "        features['FNCG_IND'] * 10 +        # 理财\n",
    "        features['FUND_IND'] * 9 +         # 基金\n",
    "        features['INSUR_IND'] * 8 +        # 保险\n",
    "        features['CCARD_IND'] * 7 +        # 信用卡\n",
    "        features['BOND_IND'] * 6 +         # 国债\n",
    "        features['METAL_IND'] * 5 +         # 贵金属\n",
    "        features['DP_IND'] * 4 +           # 存款\n",
    "        features['DCARD_IND'] * 3 +        # 借记卡\n",
    "        features['TPAY_CCARD_IND'] * 2 +   # 三方支付绑信用卡\n",
    "        features['TPAY_DCARD_IND'] * 2 +   # 三方支付绑借记卡\n",
    "        features['TPAY_WX_IND'] * 1 +      # 微信支付\n",
    "        features['TPAY_ALI_IND'] * 1       # 支付宝\n",
    "    )\n",
    "    \n",
    "    # 投资价值评分\n",
    "    features['PROD_INVEST_SCORE'] = (\n",
    "        features['FNCG_IND'] * 10 + \n",
    "        features['FUND_IND'] * 9 + \n",
    "        features['INSUR_IND'] * 8 + \n",
    "        features['BOND_IND'] * 7 + \n",
    "        features['METAL_IND'] * 6\n",
    "    )\n",
    "    \n",
    "    # 5. 产品活跃度\n",
    "    print(\"计算产品活跃度...\")\n",
    "    \n",
    "    # 投资活跃度\n",
    "    features['PROD_INVEST_ACTIVE'] = (features['FNCG_IND'] + features['FUND_IND'] + \n",
    "                                       features['BOND_IND'] + features['METAL_IND'])\n",
    "    \n",
    "    # 支付活跃度\n",
    "    features['PROD_PAY_ACTIVE'] = (features['TPAY_DCARD_IND'] + features['TPAY_CCARD_IND'] + \n",
    "                                    features['TPAY_WX_IND'] + features['TPAY_ALI_IND'])\n",
    "    \n",
    "    # 综合活跃度\n",
    "    features['PROD_OVERALL_ACTIVE'] = features['PROD_TOTAL_CNT'] + features['PROD_VALUE_SCORE'] / 10\n",
    "    \n",
    "    # 6. 产品覆盖率\n",
    "    print(\"计算产品覆盖率...\")\n",
    "    \n",
    "    total_products = len(product_cols)\n",
    "    features['PROD_COVERAGE_RATE'] = features['PROD_TOTAL_CNT'] / total_products\n",
    "    features['PROD_WEALTH_COVERAGE'] = features['PROD_WEALTH_CNT'] / 5  # 5种理财产品\n",
    "    features['PROD_TPAY_COVERAGE'] = features['PROD_TPAY_CNT'] / 4  # 4种支付方式\n",
    "    \n",
    "    # 7. 客户分层特征\n",
    "    print(\"生成客户分层特征...\")\n",
    "    \n",
    "    # 按价值评分分层\n",
    "    features['PROD_VALUE_RANK'] = features['PROD_VALUE_SCORE'].rank(pct=True)\n",
    "    features['PROD_IS_HIGH_VALUE'] = (features['PROD_VALUE_RANK'] >= 0.75).astype(int)\n",
    "    features['PROD_IS_MID_VALUE'] = ((features['PROD_VALUE_RANK'] >= 0.25) & (features['PROD_VALUE_RANK'] < 0.75)).astype(int)\n",
    "    features['PROD_IS_LOW_VALUE'] = (features['PROD_VALUE_RANK'] < 0.25).astype(int)\n",
    "    \n",
    "    # 按产品数量分层\n",
    "    features['PROD_CNT_RANK'] = features['PROD_TOTAL_CNT'].rank(pct=True)\n",
    "    features['PROD_IS_ACTIVE_USER'] = (features['PROD_CNT_RANK'] >= 0.75).astype(int)\n",
    "    features['PROD_IS_INACTIVE_USER'] = (features['PROD_CNT_RANK'] < 0.25).astype(int)\n",
    "    \n",
    "    # 8. 客户类型标识\n",
    "    print(\"生成客户类型标识...\")\n",
    "    \n",
    "    # 高端客户: 有理财+基金+保险\n",
    "    features['PROD_IS_PREMIUM'] = ((features['FNCG_IND'] == 1) & (features['FUND_IND'] == 1) & \n",
    "                                    (features['INSUR_IND'] == 1)).astype(int)\n",
    "    \n",
    "    # 基础客户: 只有存款和借记卡\n",
    "    features['PROD_IS_BASIC'] = ((features['DP_IND'] == 1) & (features['DCARD_IND'] == 1) & \n",
    "                                  (features['PROD_TOTAL_CNT'] <= 3)).astype(int)\n",
    "    \n",
    "    # 信贷客户\n",
    "    features['PROD_IS_CREDIT_CUSTOMER'] = ((features['LOAN_IND'] == 1) | (features['CCARD_IND'] == 1)).astype(int)\n",
    "    \n",
    "    # 投资客户\n",
    "    features['PROD_IS_INVESTOR'] = (features['PROD_WEALTH_CNT'] >= 2).astype(int)\n",
    "    \n",
    "    # 数字客户: 使用第三方支付\n",
    "    features['PROD_IS_DIGITAL_USER'] = (features['PROD_TPAY_CNT'] >= 2).astype(int)\n",
    "    \n",
    "    # 全能客户: 各类产品都有\n",
    "    features['PROD_IS_ALL_ROUND'] = ((features['DP_IND'] == 1) & (features['LOAN_IND'] == 1) & \n",
    "                                      (features['PROD_WEALTH_CNT'] >= 2) & (features['PROD_CARD_CNT'] >= 1)).astype(int)\n",
    "    \n",
    "    # 9. 产品多样性\n",
    "    print(\"计算产品多样性...\")\n",
    "    \n",
    "    # 是否多元化投资\n",
    "    features['PROD_IS_DIVERSIFIED_INVEST'] = (features['PROD_INVEST_ACTIVE'] >= 3).astype(int)\n",
    "    \n",
    "    # 是否多元化支付\n",
    "    features['PROD_IS_DIVERSIFIED_PAY'] = (features['PROD_PAY_ACTIVE'] >= 3).astype(int)\n",
    "    \n",
    "    # 是否全渠道客户\n",
    "    features['PROD_IS_OMNICHANNEL'] = ((features['PROD_WEALTH_CNT'] >= 1) & \n",
    "                                        (features['PROD_TPAY_CNT'] >= 1) & \n",
    "                                        (features['PROD_CARD_CNT'] >= 1)).astype(int)\n",
    "    \n",
    "    # 10. 产品比率特征\n",
    "    print(\"计算产品比率特征...\")\n",
    "    \n",
    "    # 理财产品占比\n",
    "    features['PROD_WEALTH_RATIO'] = features['PROD_WEALTH_CNT'] / (features['PROD_TOTAL_CNT'] + 1)\n",
    "    \n",
    "    # 支付产品占比\n",
    "    features['PROD_TPAY_RATIO'] = features['PROD_TPAY_CNT'] / (features['PROD_TOTAL_CNT'] + 1)\n",
    "    \n",
    "    # 卡产品占比\n",
    "    features['PROD_CARD_RATIO'] = features['PROD_CARD_CNT'] / (features['PROD_TOTAL_CNT'] + 1)\n",
    "    \n",
    "    # 投资价值占总价值比\n",
    "    features['PROD_INVEST_VALUE_RATIO'] = features['PROD_INVEST_SCORE'] / (features['PROD_VALUE_SCORE'] + 1)\n",
    "    \n",
    "    # 11. 特征交互\n",
    "    print(\"生成产品交互特征...\")\n",
    "    \n",
    "    # 理财数量与卡数量交互\n",
    "    features['PROD_WEALTH_CARD_INTER'] = features['PROD_WEALTH_CNT'] * features['PROD_CARD_CNT']\n",
    "    \n",
    "    # 理财数量与支付数量交互\n",
    "    features['PROD_WEALTH_TPAY_INTER'] = features['PROD_WEALTH_CNT'] * features['PROD_TPAY_CNT']\n",
    "    \n",
    "    # 贷款与存款交互\n",
    "    features['PROD_LOAN_DP_INTER'] = features['LOAN_IND'] * features['DP_IND']\n",
    "    \n",
    "    # 信用卡与第三方支付交互\n",
    "    features['PROD_CCARD_TPAY_INTER'] = features['CCARD_IND'] * features['PROD_TPAY_CNT']\n",
    "    \n",
    "    # 12. 潜力评分\n",
    "    print(\"计算客户潜力评分...\")\n",
    "    \n",
    "    # 信用卡潜力评分(针对本赛题)\n",
    "    features['PROD_CCARD_POTENTIAL'] = (\n",
    "        (1 - features['CCARD_IND']) * 20 +  # 没有信用卡的潜力更大\n",
    "        features['DP_IND'] * 10 +             # 有存款\n",
    "        features['DCARD_IND'] * 8 +           # 有借记卡\n",
    "        features['PROD_WEALTH_CNT'] * 5 +     # 理财产品越多潜力越大\n",
    "        features['PROD_TPAY_CNT'] * 3 +       # 使用第三方支付\n",
    "        features['LOAN_IND'] * 5              # 有贷款\n",
    "    )\n",
    "    \n",
    "    # 综合潜力评分\n",
    "    features['PROD_TOTAL_POTENTIAL'] = (\n",
    "        features['PROD_VALUE_SCORE'] + \n",
    "        features['PROD_CCARD_POTENTIAL'] + \n",
    "        features['PROD_TOTAL_CNT'] * 2\n",
    "    )\n",
    "    \n",
    "    # 删除原始列\n",
    "    drop_cols = ['DATA_DAT']\n",
    "    features = features.drop(columns=[col for col in drop_cols if col in features.columns])\n",
    "    \n",
    "    print(f\"处理后特征维度: {features.shape}\")\n",
    "    print(f\"新增特征数: {features.shape[1] - 1}\")  # 减去CUST_NO\n",
    "    \n",
    "    return features\n",
    "\n",
    "# 处理训练集和测试集\n",
    "TRAIN_PROD_HOLD_features = process_prod_hold_features(TRAIN_PROD_HOLD_features)\n",
    "print(\"\\n\")\n",
    "A_PROD_HOLD_features = process_prod_hold_features(A_PROD_HOLD_features)\n",
    "\n",
    "print(\"\\n\" + \"=\"*100)\n",
    "print(\"产品持有信息表特征工程完成!\")\n",
    "print(\"=\"*100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "acd0a6f6",
   "metadata": {},
   "source": [
    "## 4. 掌银客户信息表特征工程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "ee04dd0f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "====================================================================================================\n",
      "开始处理掌银客户信息表 (MB_CUST_INFO)\n",
      "====================================================================================================\n",
      "原始数据维度: (500, 10)\n",
      "处理基础特征...\n",
      "处理注册时长特征...\n",
      "处理登录活跃度特征...\n",
      "处理时长特征...\n",
      "处理时间趋势特征...\n",
      "计算综合活跃度评分...\n",
      "生成用户分层特征...\n",
      "生成使用习惯特征...\n",
      "处理客户类型特征...\n",
      "生成交互特征...\n",
      "生成标准化特征...\n",
      "计算使用稳定性特征...\n",
      "计算信用卡营销潜力评分...\n",
      "处理后特征维度: (500, 77)\n",
      "新增特征数: 76\n",
      "\n",
      "\n",
      "原始数据维度: (500, 10)\n",
      "处理基础特征...\n",
      "处理注册时长特征...\n",
      "处理登录活跃度特征...\n",
      "处理时长特征...\n",
      "处理时间趋势特征...\n",
      "计算综合活跃度评分...\n",
      "生成用户分层特征...\n",
      "生成使用习惯特征...\n",
      "处理客户类型特征...\n",
      "生成交互特征...\n",
      "生成标准化特征...\n",
      "计算使用稳定性特征...\n",
      "计算信用卡营销潜力评分...\n",
      "处理后特征维度: (500, 77)\n",
      "新增特征数: 76\n",
      "\n",
      "====================================================================================================\n",
      "掌银客户信息表特征工程完成!\n",
      "====================================================================================================\n"
     ]
    }
   ],
   "source": [
    "print(\"=\"*100)\n",
    "print(\"开始处理掌银客户信息表 (MB_CUST_INFO)\")\n",
    "print(\"=\"*100)\n",
    "\n",
    "# 训练集\n",
    "TRAIN_MB_CUST_INFO_features = TRAIN_MB_CUST_INFO_data.copy()\n",
    "# 测试集\n",
    "A_MB_CUST_INFO_features = A_MB_CUST_INFO_data.copy()\n",
    "\n",
    "def process_mb_cust_info_features(df):\n",
    "    \"\"\"\n",
    "    掌银客户信息表特征工程\n",
    "    包含: 掌银注册时间、客户类型、登录天数、活跃天数、浏览时长等\n",
    "    \"\"\"\n",
    "    print(f\"原始数据维度: {df.shape}\")\n",
    "    \n",
    "    features = df.copy()\n",
    "    \n",
    "    # 填充缺失值\n",
    "    for col in features.columns:\n",
    "        if col not in ['CUST_NO', 'DATA_DAT']:\n",
    "            features[col] = features[col].fillna(0)\n",
    "    \n",
    "    # 1. 基础特征\n",
    "    print(\"处理基础特征...\")\n",
    "    \n",
    "    # 掌银注册时间\n",
    "    features['MB_REG_TIME'] = features['MB_REG_TIME']\n",
    "    \n",
    "    # 掌银客户类型 - 转换为数值类型\n",
    "    if features['MB_CUST_TYPE'].dtype == object:\n",
    "        # 如果是字符串,尝试转换为数值\n",
    "        features['MB_CUST_TYPE'] = pd.to_numeric(features['MB_CUST_TYPE'], errors='coerce').fillna(0)\n",
    "    \n",
    "    # 登录天数\n",
    "    features['MB_LOGIN_1M'] = features['MB_LOGIN_CNT_1M']\n",
    "    features['MB_LOGIN_3M'] = features['MB_LOGIN_CNT_3M']\n",
    "    \n",
    "    # 活跃天数\n",
    "    features['MB_ACTV_1M'] = features['MB_ACTV_CNT_1M']\n",
    "    features['MB_ACTV_3M'] = features['MB_ACTV_CNT_3M']\n",
    "    \n",
    "    # 浏览时长\n",
    "    features['MB_VIEW_1M'] = features['VIEW_MINUTE_1M']\n",
    "    features['MB_VIEW_3M'] = features['VIEW_MINUTE_3M']\n",
    "    \n",
    "    # 2. 注册时长特征\n",
    "    print(\"处理注册时长特征...\")\n",
    "    \n",
    "    # 是否老用户\n",
    "    features['MB_IS_OLD_USER'] = (features['MB_REG_TIME'] >= 365).astype(int)\n",
    "    features['MB_IS_NEW_USER'] = (features['MB_REG_TIME'] <= 30).astype(int)\n",
    "    features['MB_IS_MID_USER'] = ((features['MB_REG_TIME'] > 30) & (features['MB_REG_TIME'] < 365)).astype(int)\n",
    "    \n",
    "    # 注册时长分段 - 修复NaN转int问题\n",
    "    reg_time_group = pd.cut(\n",
    "        features['MB_REG_TIME'], \n",
    "        bins=[0, 30, 90, 180, 365, 730, 10000], \n",
    "        labels=[1, 2, 3, 4, 5, 6]\n",
    "    )\n",
    "    features['MB_REG_TIME_GROUP'] = reg_time_group.astype(object).fillna(0).astype(int)\n",
    "    \n",
    "    # 注册时长对数变换\n",
    "    features['MB_REG_TIME_LOG'] = np.log1p(features['MB_REG_TIME'])\n",
    "    \n",
    "    # 3. 登录活跃度特征\n",
    "    print(\"处理登录活跃度特征...\")\n",
    "    \n",
    "    # 登录活跃率\n",
    "    features['MB_ACTV_LOGIN_RATIO_1M'] = features['MB_ACTV_1M'] / (features['MB_LOGIN_1M'] + 1)\n",
    "    features['MB_ACTV_LOGIN_RATIO_3M'] = features['MB_ACTV_3M'] / (features['MB_LOGIN_3M'] + 1)\n",
    "    \n",
    "    # 平均每次登录活跃程度\n",
    "    features['MB_ACTV_PER_LOGIN_1M'] = features['MB_ACTV_1M'] / (features['MB_LOGIN_1M'] + 1)\n",
    "    features['MB_ACTV_PER_LOGIN_3M'] = features['MB_ACTV_3M'] / (features['MB_LOGIN_3M'] + 1)\n",
    "    \n",
    "    # 登录频率(月均)\n",
    "    features['MB_LOGIN_FREQ_1M'] = features['MB_LOGIN_1M'] / 30\n",
    "    features['MB_LOGIN_FREQ_3M'] = features['MB_LOGIN_3M'] / 90\n",
    "    \n",
    "    # 活跃频率(月均)\n",
    "    features['MB_ACTV_FREQ_1M'] = features['MB_ACTV_1M'] / 30\n",
    "    features['MB_ACTV_FREQ_3M'] = features['MB_ACTV_3M'] / 90\n",
    "    \n",
    "    # 4. 时长特征\n",
    "    print(\"处理时长特征...\")\n",
    "    \n",
    "    # 平均每天浏览时长\n",
    "    features['MB_VIEW_PER_DAY_1M'] = features['MB_VIEW_1M'] / 30\n",
    "    features['MB_VIEW_PER_DAY_3M'] = features['MB_VIEW_3M'] / 90\n",
    "    \n",
    "    # 平均每次登录浏览时长\n",
    "    features['MB_VIEW_PER_LOGIN_1M'] = features['MB_VIEW_1M'] / (features['MB_LOGIN_1M'] + 1)\n",
    "    features['MB_VIEW_PER_LOGIN_3M'] = features['MB_VIEW_3M'] / (features['MB_LOGIN_3M'] + 1)\n",
    "    \n",
    "    # 平均每次活跃浏览时长\n",
    "    features['MB_VIEW_PER_ACTV_1M'] = features['MB_VIEW_1M'] / (features['MB_ACTV_1M'] + 1)\n",
    "    features['MB_VIEW_PER_ACTV_3M'] = features['MB_VIEW_3M'] / (features['MB_ACTV_3M'] + 1)\n",
    "    \n",
    "    # 时长对数变换\n",
    "    features['MB_VIEW_1M_LOG'] = np.log1p(features['MB_VIEW_1M'])\n",
    "    features['MB_VIEW_3M_LOG'] = np.log1p(features['MB_VIEW_3M'])\n",
    "    \n",
    "    # 5. 时间趋势特征\n",
    "    print(\"处理时间趋势特征...\")\n",
    "    \n",
    "    # 登录天数变化\n",
    "    features['MB_LOGIN_DIFF_1M_3M'] = features['MB_LOGIN_3M'] / 3 - features['MB_LOGIN_1M']\n",
    "    features['MB_LOGIN_GROWTH_1M_3M'] = (features['MB_LOGIN_3M'] / 3 - features['MB_LOGIN_1M']) / (features['MB_LOGIN_1M'] + 1)\n",
    "    \n",
    "    # 活跃天数变化\n",
    "    features['MB_ACTV_DIFF_1M_3M'] = features['MB_ACTV_3M'] / 3 - features['MB_ACTV_1M']\n",
    "    features['MB_ACTV_GROWTH_1M_3M'] = (features['MB_ACTV_3M'] / 3 - features['MB_ACTV_1M']) / (features['MB_ACTV_1M'] + 1)\n",
    "    \n",
    "    # 浏览时长变化\n",
    "    features['MB_VIEW_DIFF_1M_3M'] = features['MB_VIEW_3M'] / 3 - features['MB_VIEW_1M']\n",
    "    features['MB_VIEW_GROWTH_1M_3M'] = (features['MB_VIEW_3M'] / 3 - features['MB_VIEW_1M']) / (features['MB_VIEW_1M'] + 1)\n",
    "    \n",
    "    # 是否上升趋势\n",
    "    features['MB_LOGIN_TREND_UP'] = (features['MB_LOGIN_DIFF_1M_3M'] > 0).astype(int)\n",
    "    features['MB_ACTV_TREND_UP'] = (features['MB_ACTV_DIFF_1M_3M'] > 0).astype(int)\n",
    "    features['MB_VIEW_TREND_UP'] = (features['MB_VIEW_DIFF_1M_3M'] > 0).astype(int)\n",
    "    \n",
    "    # 是否下降趋势\n",
    "    features['MB_LOGIN_TREND_DOWN'] = (features['MB_LOGIN_DIFF_1M_3M'] < 0).astype(int)\n",
    "    features['MB_ACTV_TREND_DOWN'] = (features['MB_ACTV_DIFF_1M_3M'] < 0).astype(int)\n",
    "    features['MB_VIEW_TREND_DOWN'] = (features['MB_VIEW_DIFF_1M_3M'] < 0).astype(int)\n",
    "    \n",
    "    # 6. 综合活跃度评分\n",
    "    print(\"计算综合活跃度评分...\")\n",
    "    \n",
    "    # 基础活跃度评分\n",
    "    features['MB_ACTIVE_SCORE_1M'] = (\n",
    "        features['MB_LOGIN_1M'] * 1 + \n",
    "        features['MB_ACTV_1M'] * 2 + \n",
    "        features['MB_VIEW_1M'] * 0.01\n",
    "    )\n",
    "    \n",
    "    features['MB_ACTIVE_SCORE_3M'] = (\n",
    "        features['MB_LOGIN_3M'] * 1 + \n",
    "        features['MB_ACTV_3M'] * 2 + \n",
    "        features['MB_VIEW_3M'] * 0.01\n",
    "    )\n",
    "    \n",
    "    # 加权活跃度评分(考虑注册时长)\n",
    "    features['MB_WEIGHTED_SCORE_1M'] = features['MB_ACTIVE_SCORE_1M'] * np.log1p(features['MB_REG_TIME'])\n",
    "    features['MB_WEIGHTED_SCORE_3M'] = features['MB_ACTIVE_SCORE_3M'] * np.log1p(features['MB_REG_TIME'])\n",
    "    \n",
    "    # 7. 用户分层特征\n",
    "    print(\"生成用户分层特征...\")\n",
    "    \n",
    "    # 按活跃度评分分层\n",
    "    features['MB_SCORE_RANK_1M'] = features['MB_ACTIVE_SCORE_1M'].rank(pct=True)\n",
    "    features['MB_SCORE_RANK_3M'] = features['MB_ACTIVE_SCORE_3M'].rank(pct=True)\n",
    "    \n",
    "    features['MB_IS_SUPER_ACTIVE_1M'] = (features['MB_SCORE_RANK_1M'] >= 0.9).astype(int)\n",
    "    features['MB_IS_HIGH_ACTIVE_1M'] = ((features['MB_SCORE_RANK_1M'] >= 0.75) & (features['MB_SCORE_RANK_1M'] < 0.9)).astype(int)\n",
    "    features['MB_IS_MID_ACTIVE_1M'] = ((features['MB_SCORE_RANK_1M'] >= 0.25) & (features['MB_SCORE_RANK_1M'] < 0.75)).astype(int)\n",
    "    features['MB_IS_LOW_ACTIVE_1M'] = (features['MB_SCORE_RANK_1M'] < 0.25).astype(int)\n",
    "    \n",
    "    # 8. 使用习惯特征\n",
    "    print(\"生成使用习惯特征...\")\n",
    "    \n",
    "    # 是否高频用户\n",
    "    features['MB_IS_HIGH_FREQ'] = (features['MB_LOGIN_1M'] >= 20).astype(int)\n",
    "    \n",
    "    # 是否深度用户(登录后都会活跃)\n",
    "    features['MB_IS_DEEP_USER'] = (features['MB_ACTV_LOGIN_RATIO_1M'] >= 0.8).astype(int)\n",
    "    \n",
    "    # 是否长时间用户\n",
    "    features['MB_IS_LONG_TIME_USER'] = (features['MB_VIEW_PER_LOGIN_1M'] >= 10).astype(int)\n",
    "    \n",
    "    # 是否流失风险用户\n",
    "    features['MB_IS_CHURN_RISK'] = ((features['MB_LOGIN_1M'] <= 2) & (features['MB_REG_TIME'] >= 180)).astype(int)\n",
    "    \n",
    "    # 是否沉睡用户\n",
    "    features['MB_IS_SLEEP_USER'] = ((features['MB_LOGIN_1M'] == 0) & (features['MB_LOGIN_3M'] <= 3)).astype(int)\n",
    "    \n",
    "    # 是否激活用户(新用户且活跃)\n",
    "    features['MB_IS_ACTIVATED_USER'] = ((features['MB_IS_NEW_USER'] == 1) & (features['MB_LOGIN_1M'] >= 10)).astype(int)\n",
    "    \n",
    "    # 9. 客户类型特征\n",
    "    print(\"处理客户类型特征...\")\n",
    "    \n",
    "    # 客户类型是否高级 - MB_CUST_TYPE已转换为数值\n",
    "    features['MB_IS_PREMIUM_TYPE'] = (features['MB_CUST_TYPE'] >= 3).astype(int)\n",
    "    features['MB_IS_BASIC_TYPE'] = (features['MB_CUST_TYPE'] <= 1).astype(int)\n",
    "    \n",
    "    # 10. 交互特征\n",
    "    print(\"生成交互特征...\")\n",
    "    \n",
    "    # 注册时长与活跃度交互\n",
    "    features['MB_REG_ACTV_1M'] = features['MB_REG_TIME'] * features['MB_ACTV_1M']\n",
    "    features['MB_REG_ACTV_3M'] = features['MB_REG_TIME'] * features['MB_ACTV_3M']\n",
    "    \n",
    "    # 注册时长与登录次数交互\n",
    "    features['MB_REG_LOGIN_1M'] = features['MB_REG_TIME'] * features['MB_LOGIN_1M']\n",
    "    features['MB_REG_LOGIN_3M'] = features['MB_REG_TIME'] * features['MB_LOGIN_3M']\n",
    "    \n",
    "    # 客户类型与活跃度交互\n",
    "    features['MB_TYPE_ACTV_1M'] = features['MB_CUST_TYPE'] * features['MB_ACTV_1M']\n",
    "    features['MB_TYPE_ACTV_3M'] = features['MB_CUST_TYPE'] * features['MB_ACTV_3M']\n",
    "    \n",
    "    # 登录与浏览时长交互\n",
    "    features['MB_LOGIN_VIEW_1M'] = features['MB_LOGIN_1M'] * features['MB_VIEW_1M']\n",
    "    features['MB_LOGIN_VIEW_3M'] = features['MB_LOGIN_3M'] * features['MB_VIEW_3M']\n",
    "    \n",
    "    # 11. 标准化特征\n",
    "    print(\"生成标准化特征...\")\n",
    "    \n",
    "    # 登录天数标准化\n",
    "    features['MB_LOGIN_1M_NORM'] = (features['MB_LOGIN_1M'] - features['MB_LOGIN_1M'].mean()) / (features['MB_LOGIN_1M'].std() + 1e-5)\n",
    "    features['MB_LOGIN_3M_NORM'] = (features['MB_LOGIN_3M'] - features['MB_LOGIN_3M'].mean()) / (features['MB_LOGIN_3M'].std() + 1e-5)\n",
    "    \n",
    "    # 活跃天数标准化\n",
    "    features['MB_ACTV_1M_NORM'] = (features['MB_ACTV_1M'] - features['MB_ACTV_1M'].mean()) / (features['MB_ACTV_1M'].std() + 1e-5)\n",
    "    features['MB_ACTV_3M_NORM'] = (features['MB_ACTV_3M'] - features['MB_ACTV_3M'].mean()) / (features['MB_ACTV_3M'].std() + 1e-5)\n",
    "    \n",
    "    # 浏览时长标准化\n",
    "    features['MB_VIEW_1M_NORM'] = (features['MB_VIEW_1M'] - features['MB_VIEW_1M'].mean()) / (features['MB_VIEW_1M'].std() + 1e-5)\n",
    "    features['MB_VIEW_3M_NORM'] = (features['MB_VIEW_3M'] - features['MB_VIEW_3M'].mean()) / (features['MB_VIEW_3M'].std() + 1e-5)\n",
    "    \n",
    "    # 12. 使用稳定性特征\n",
    "    print(\"计算使用稳定性特征...\")\n",
    "    \n",
    "    # 登录稳定性\n",
    "    features['MB_LOGIN_STABILITY'] = features['MB_LOGIN_1M'] / (features['MB_LOGIN_3M'] / 3 + 1)\n",
    "    \n",
    "    # 活跃稳定性\n",
    "    features['MB_ACTV_STABILITY'] = features['MB_ACTV_1M'] / (features['MB_ACTV_3M'] / 3 + 1)\n",
    "    \n",
    "    # 浏览稳定性\n",
    "    features['MB_VIEW_STABILITY'] = features['MB_VIEW_1M'] / (features['MB_VIEW_3M'] / 3 + 1)\n",
    "    \n",
    "    # 综合稳定性\n",
    "    features['MB_OVERALL_STABILITY'] = (features['MB_LOGIN_STABILITY'] + \n",
    "                                        features['MB_ACTV_STABILITY'] + \n",
    "                                        features['MB_VIEW_STABILITY']) / 3\n",
    "    \n",
    "    # 13. 潜力评分(针对信用卡营销)\n",
    "    print(\"计算信用卡营销潜力评分...\")\n",
    "    \n",
    "    features['MB_CCARD_POTENTIAL'] = (\n",
    "        features['MB_ACTIVE_SCORE_1M'] * 0.3 +      # 近期活跃度\n",
    "        features['MB_ACTIVE_SCORE_3M'] * 0.2 +      # 中期活跃度\n",
    "        features['MB_REG_TIME_LOG'] * 10 +          # 注册时长\n",
    "        features['MB_IS_DEEP_USER'] * 20 +          # 深度用户\n",
    "        features['MB_IS_HIGH_FREQ'] * 15 +          # 高频用户\n",
    "        features['MB_ACTV_LOGIN_RATIO_1M'] * 30 +   # 活跃率\n",
    "        features['MB_IS_PREMIUM_TYPE'] * 25         # 高级客户类型\n",
    "    )\n",
    "    \n",
    "    # 删除原始列\n",
    "    drop_cols = ['DATA_DAT', 'MB_REG_TIME', 'MB_CUST_TYPE', \n",
    "                 'MB_LOGIN_CNT_1M', 'MB_LOGIN_CNT_3M', \n",
    "                 'MB_ACTV_CNT_1M', 'MB_ACTV_CNT_3M',\n",
    "                 'VIEW_MINUTE_1M', 'VIEW_MINUTE_3M']\n",
    "    features = features.drop(columns=[col for col in drop_cols if col in features.columns])\n",
    "    \n",
    "    print(f\"处理后特征维度: {features.shape}\")\n",
    "    print(f\"新增特征数: {features.shape[1] - 1}\")  # 减去CUST_NO\n",
    "    \n",
    "    return features\n",
    "\n",
    "# 处理训练集和测试集\n",
    "TRAIN_MB_CUST_INFO_features = process_mb_cust_info_features(TRAIN_MB_CUST_INFO_features)\n",
    "print(\"\\n\")\n",
    "A_MB_CUST_INFO_features = process_mb_cust_info_features(A_MB_CUST_INFO_features)\n",
    "\n",
    "print(\"\\n\" + \"=\"*100)\n",
    "print(\"掌银客户信息表特征工程完成!\")\n",
    "print(\"=\"*100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b05b555f",
   "metadata": {},
   "source": [
    "## 5. 保存特征文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "ad34aae3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "====================================================================================================\n",
      "开始保存特征文件\n",
      "====================================================================================================\n",
      "\n",
      "保存训练集特征...\n",
      "  - TRAIN_NATURE_features.pkl 已保存, 维度: (1000, 91)\n",
      "  - TRAIN_ASSET_features.pkl 已保存, 维度: (1000, 178)\n",
      "  - TRAIN_PROD_HOLD_features.pkl 已保存, 维度: (1000, 75)\n",
      "  - TRAIN_MB_CUST_INFO_features.pkl 已保存, 维度: (500, 77)\n",
      "\n",
      "保存测试集特征...\n",
      "  - A_NATURE_features.pkl 已保存, 维度: (500, 88)\n",
      "  - A_ASSET_features.pkl 已保存, 维度: (500, 178)\n",
      "  - A_PROD_HOLD_features.pkl 已保存, 维度: (500, 75)\n",
      "  - A_MB_CUST_INFO_features.pkl 已保存, 维度: (500, 77)\n",
      "\n",
      "====================================================================================================\n",
      "所有特征文件保存完成!\n",
      "====================================================================================================\n",
      "\n",
      "====================================================================================================\n",
      "特征统计信息\n",
      "====================================================================================================\n",
      "\n",
      "训练集:\n",
      "  - 自然属性特征数: 90\n",
      "  - 资产负债特征数: 177\n",
      "  - 产品持有特征数: 74\n",
      "  - 掌银信息特征数: 76\n",
      "  - 总特征数: 417\n",
      "\n",
      "测试集:\n",
      "  - 自然属性特征数: 87\n",
      "  - 资产负债特征数: 177\n",
      "  - 产品持有特征数: 74\n",
      "  - 掌银信息特征数: 76\n",
      "  - 总特征数: 414\n"
     ]
    }
   ],
   "source": [
    "print(\"=\"*100)\n",
    "print(\"开始保存特征文件\")\n",
    "print(\"=\"*100)\n",
    "\n",
    "# 创建feature目录\n",
    "feature_dir = './feature'\n",
    "if not os.path.exists(feature_dir):\n",
    "    os.makedirs(feature_dir)\n",
    "    print(f\"创建特征目录: {feature_dir}\")\n",
    "\n",
    "# 保存训练集特征\n",
    "print(\"\\n保存训练集特征...\")\n",
    "with open(os.path.join(feature_dir, 'TRAIN_NATURE_features.pkl'), 'wb') as f:\n",
    "    pickle.dump(TRAIN_NATURE_features, f)\n",
    "print(f\"  - TRAIN_NATURE_features.pkl 已保存, 维度: {TRAIN_NATURE_features.shape}\")\n",
    "\n",
    "with open(os.path.join(feature_dir, 'TRAIN_ASSET_features.pkl'), 'wb') as f:\n",
    "    pickle.dump(TRAIN_ASSET_features, f)\n",
    "print(f\"  - TRAIN_ASSET_features.pkl 已保存, 维度: {TRAIN_ASSET_features.shape}\")\n",
    "\n",
    "with open(os.path.join(feature_dir, 'TRAIN_PROD_HOLD_features.pkl'), 'wb') as f:\n",
    "    pickle.dump(TRAIN_PROD_HOLD_features, f)\n",
    "print(f\"  - TRAIN_PROD_HOLD_features.pkl 已保存, 维度: {TRAIN_PROD_HOLD_features.shape}\")\n",
    "\n",
    "with open(os.path.join(feature_dir, 'TRAIN_MB_CUST_INFO_features.pkl'), 'wb') as f:\n",
    "    pickle.dump(TRAIN_MB_CUST_INFO_features, f)\n",
    "print(f\"  - TRAIN_MB_CUST_INFO_features.pkl 已保存, 维度: {TRAIN_MB_CUST_INFO_features.shape}\")\n",
    "\n",
    "# 保存测试集特征\n",
    "print(\"\\n保存测试集特征...\")\n",
    "with open(os.path.join(feature_dir, 'A_NATURE_features.pkl'), 'wb') as f:\n",
    "    pickle.dump(A_NATURE_features, f)\n",
    "print(f\"  - A_NATURE_features.pkl 已保存, 维度: {A_NATURE_features.shape}\")\n",
    "\n",
    "with open(os.path.join(feature_dir, 'A_ASSET_features.pkl'), 'wb') as f:\n",
    "    pickle.dump(A_ASSET_features, f)\n",
    "print(f\"  - A_ASSET_features.pkl 已保存, 维度: {A_ASSET_features.shape}\")\n",
    "\n",
    "with open(os.path.join(feature_dir, 'A_PROD_HOLD_features.pkl'), 'wb') as f:\n",
    "    pickle.dump(A_PROD_HOLD_features, f)\n",
    "print(f\"  - A_PROD_HOLD_features.pkl 已保存, 维度: {A_PROD_HOLD_features.shape}\")\n",
    "\n",
    "with open(os.path.join(feature_dir, 'A_MB_CUST_INFO_features.pkl'), 'wb') as f:\n",
    "    pickle.dump(A_MB_CUST_INFO_features, f)\n",
    "print(f\"  - A_MB_CUST_INFO_features.pkl 已保存, 维度: {A_MB_CUST_INFO_features.shape}\")\n",
    "\n",
    "print(\"\\n\" + \"=\"*100)\n",
    "print(\"所有特征文件保存完成!\")\n",
    "print(\"=\"*100)\n",
    "\n",
    "# 输出特征统计信息\n",
    "print(\"\\n\" + \"=\"*100)\n",
    "print(\"特征统计信息\")\n",
    "print(\"=\"*100)\n",
    "print(f\"\\n训练集:\")\n",
    "print(f\"  - 自然属性特征数: {TRAIN_NATURE_features.shape[1] - 1}\")\n",
    "print(f\"  - 资产负债特征数: {TRAIN_ASSET_features.shape[1] - 1}\")\n",
    "print(f\"  - 产品持有特征数: {TRAIN_PROD_HOLD_features.shape[1] - 1}\")\n",
    "print(f\"  - 掌银信息特征数: {TRAIN_MB_CUST_INFO_features.shape[1] - 1}\")\n",
    "print(f\"  - 总特征数: {(TRAIN_NATURE_features.shape[1] - 1) + (TRAIN_ASSET_features.shape[1] - 1) + (TRAIN_PROD_HOLD_features.shape[1] - 1) + (TRAIN_MB_CUST_INFO_features.shape[1] - 1)}\")\n",
    "\n",
    "print(f\"\\n测试集:\")\n",
    "print(f\"  - 自然属性特征数: {A_NATURE_features.shape[1] - 1}\")\n",
    "print(f\"  - 资产负债特征数: {A_ASSET_features.shape[1] - 1}\")\n",
    "print(f\"  - 产品持有特征数: {A_PROD_HOLD_features.shape[1] - 1}\")\n",
    "print(f\"  - 掌银信息特征数: {A_MB_CUST_INFO_features.shape[1] - 1}\")\n",
    "print(f\"  - 总特征数: {(A_NATURE_features.shape[1] - 1) + (A_ASSET_features.shape[1] - 1) + (A_PROD_HOLD_features.shape[1] - 1) + (A_MB_CUST_INFO_features.shape[1] - 1)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "306720f1",
   "metadata": {},
   "source": [
    "## 6. 特征预览"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "ea1b7fa2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "====================================================================================================\n",
      "特征预览\n",
      "====================================================================================================\n",
      "\n",
      "【自然属性特征】前5行:\n",
      "                            CUST_NO   AGE  NATURE_AGE_GROUP_FINE  \\\n",
      "0  39217537b31b96c7a5fdadd6098dccce  35.0                      3   \n",
      "1  f114d5366174b2ee671d48119cf66b43  36.0                      4   \n",
      "2  e9c04f5fdbe13f28dfbe1592711b860b  27.0                      2   \n",
      "3  84347ed1ec767172b438afde109a9fbe  40.0                      4   \n",
      "4  165e588dbec08fc8703c75d8f692f564  31.0                      3   \n",
      "\n",
      "   NATURE_AGE_GROUP_COARSE  NATURE_AGE_SQUARE  NATURE_AGE_CUBE  \\\n",
      "0                        2             1225.0          42875.0   \n",
      "1                        2             1296.0          46656.0   \n",
      "2                        1              729.0          19683.0   \n",
      "3                        2             1600.0          64000.0   \n",
      "4                        2              961.0          29791.0   \n",
      "\n",
      "   NATURE_AGE_SQRT  NATURE_AGE_LOG  NATURE_AGE_NORMALIZED  NATURE_AGE_RANK  \\\n",
      "0         5.916080        3.583519              -1.142305           0.1510   \n",
      "1         6.000000        3.610918              -1.031088           0.1900   \n",
      "2         5.196152        3.332205              -2.032036           0.0165   \n",
      "3         6.324555        3.713572              -0.586222           0.3040   \n",
      "4         5.567764        3.465736              -1.587170           0.0690   \n",
      "\n",
      "   ...  NATURE_AGE_CUST_AGE_RATIO  NATURE_SEX_VALUE  NATURE_MARRIAGE_VALUE  \\\n",
      "0  ...                   2.204106                10                   45.0   \n",
      "1  ...                   2.574956                 5                    5.0   \n",
      "2  ...                   2.221596                 8                   -4.0   \n",
      "3  ...                   1.944075                 4                   36.0   \n",
      "4  ...                   1.985088                 2                   -2.0   \n",
      "\n",
      "   NATURE_CARD_VALUE  NATURE_CCARD_VALUE  NATURE_AGE_SEX_VALUE  \\\n",
      "0                 20                  15                 350.0   \n",
      "1                 20                  20                 180.0   \n",
      "2                 20                  16                 216.0   \n",
      "3                  4                   0                 160.0   \n",
      "4                  2                   0                  62.0   \n",
      "\n",
      "   NATURE_IS_HIGH_VALUE_YOUNG  NATURE_IS_HIGH_VALUE_MIDDLE  \\\n",
      "0                           0                            0   \n",
      "1                           0                            0   \n",
      "2                           0                            0   \n",
      "3                           0                            0   \n",
      "4                           0                            1   \n",
      "\n",
      "   NATURE_IS_OLD_CUST_HIGH_VALUE  NATURE_POTENTIAL_SCORE  \n",
      "0                              0                      23  \n",
      "1                              0                      15  \n",
      "2                              0                      18  \n",
      "3                              0                      20  \n",
      "4                              1                      30  \n",
      "\n",
      "[5 rows x 91 columns]\n",
      "\n",
      "【资产负债特征】前5行:\n",
      "                            CUST_NO        FA_BAL  FA_MAVER_BAL  \\\n",
      "0  a784637587e83d5b15ebd7f20c775eec  2.081328e+13  3.323253e+13   \n",
      "1  cd010515887ca26b16d55baf262dbf9f  1.862815e+14  1.296719e+13   \n",
      "2  adbcc7ed648e0058a23ba9d3f772caa0  1.681300e+02  1.681300e+02   \n",
      "3  f2ec4ff2068f3baa294d2684794ac2ea  3.393249e+16  3.752181e+16   \n",
      "4  4ede9dcaf02745da926ea3223c41cff4  8.889964e+11  8.889964e+11   \n",
      "\n",
      "   FA_BAL_LST_3_MTH  FA_BAL_LST_6_MTH  FA_BAL_LST_12_MTH       AUM_BAL  \\\n",
      "0      7.005451e+13      1.331450e+14       8.107885e+13  2.081328e+13   \n",
      "1      1.536401e+14      2.412862e+14       3.267778e+14  1.862815e+14   \n",
      "2      1.681300e+02      1.681300e+02       1.678300e+02  1.681300e+02   \n",
      "3      7.141463e+15      4.809644e+15       6.768266e+15  3.381748e+10   \n",
      "4      8.889964e+11      8.889964e+11       8.889964e+11  8.889964e+11   \n",
      "\n",
      "   AUM_MAVER_BAL  AUM_BAL_LST_3_MTH  AUM_BAL_LST_6_MTH  ...  ASSET_HAS_FNCG  \\\n",
      "0   3.323253e+13       7.005451e+13       1.331450e+14  ...               1   \n",
      "1   1.296719e+13       1.536401e+14       2.412862e+14  ...               0   \n",
      "2   1.681300e+02       1.681300e+02       1.681300e+02  ...               0   \n",
      "3   2.904949e+12       4.181863e+12       3.703949e+13  ...               0   \n",
      "4   8.889964e+11       8.889964e+11       8.889964e+11  ...               0   \n",
      "\n",
      "   ASSET_HAS_FUND  ASSET_HAS_INSUR  ASSET_INVEST_DIVERSITY  \\\n",
      "0               0                0                       1   \n",
      "1               1                0                       1   \n",
      "2               0                0                       0   \n",
      "3               0                0                       0   \n",
      "4               0                0                       0   \n",
      "\n",
      "   ASSET_IS_DIVERSIFIED  ASSET_AUM_LOG  ASSET_DP_LOG  ASSET_LOAN_LOG  \\\n",
      "0                     0      30.666613     30.666578        0.000000   \n",
      "1                     0      32.858280     32.852710        0.000000   \n",
      "2                     0       5.130668      5.130668        0.000000   \n",
      "3                     0      24.244244     24.244244       38.033033   \n",
      "4                     0      27.513359     27.513359        0.000000   \n",
      "\n",
      "   ASSET_AUM_SQRT  ASSET_DP_SQRT  \n",
      "0    4.562158e+06   4.562078e+06  \n",
      "1    1.364850e+07   1.361054e+07  \n",
      "2    1.296650e+01   1.296650e+01  \n",
      "3    1.838953e+05   1.838953e+05  \n",
      "4    9.428660e+05   9.428660e+05  \n",
      "\n",
      "[5 rows x 178 columns]\n",
      "\n",
      "【产品持有特征】前5行:\n",
      "                            CUST_NO  DP_IND  LOAN_IND  DCARD_IND  CCARD_IND  \\\n",
      "0  22326d5b39192704095f0acf2865e2f6       1         0          1          0   \n",
      "1  68eb04e9b0050fe52419feec71ec0291       1         0          1          0   \n",
      "2  020412ea2f34642f8557989001db9293       1         0          1          0   \n",
      "3  d2ad2671b4bd7c62f5c4844a6735fa00       1         0          1          0   \n",
      "4  ede1ab557526567b26b64e6fde05d7d0       1         0          1          0   \n",
      "\n",
      "   FNCG_IND  FUND_IND  BOND_IND  INSUR_IND  METAL_IND  ...  PROD_WEALTH_RATIO  \\\n",
      "0         0         0         0          0          0  ...                0.0   \n",
      "1         0         0         0          0          0  ...                0.0   \n",
      "2         0         0         0          0          0  ...                0.0   \n",
      "3         0         0         0          0          0  ...                0.0   \n",
      "4         0         0         0          0          0  ...                0.0   \n",
      "\n",
      "   PROD_TPAY_RATIO  PROD_CARD_RATIO  PROD_INVEST_VALUE_RATIO  \\\n",
      "0         0.571429         0.142857                      0.0   \n",
      "1         0.000000         0.333333                      0.0   \n",
      "2         0.250000         0.250000                      0.0   \n",
      "3         0.571429         0.142857                      0.0   \n",
      "4         0.500000         0.166667                      0.0   \n",
      "\n",
      "   PROD_WEALTH_CARD_INTER  PROD_WEALTH_TPAY_INTER  PROD_LOAN_DP_INTER  \\\n",
      "0                       0                       0                   0   \n",
      "1                       0                       0                   0   \n",
      "2                       0                       0                   0   \n",
      "3                       0                       0                   0   \n",
      "4                       0                       0                   0   \n",
      "\n",
      "   PROD_CCARD_TPAY_INTER  PROD_CCARD_POTENTIAL  PROD_TOTAL_POTENTIAL  \n",
      "0                      0                    50                    75  \n",
      "1                      0                    38                    49  \n",
      "2                      0                    41                    56  \n",
      "3                      0                    50                    75  \n",
      "4                      0                    47                    69  \n",
      "\n",
      "[5 rows x 75 columns]\n",
      "\n",
      "【掌银信息特征】前5行:\n",
      "                            CUST_NO  MB_LOGIN_1M  MB_LOGIN_3M  MB_ACTV_1M  \\\n",
      "0  b77e8b746f5b536d0e1da9f5123d3d9a         25.0         28.0        17.0   \n",
      "1  98233e3588864e866bce062c4ba7aec0         20.0         11.0        12.0   \n",
      "2  b7cfccb7f8d3c06cf62a84f72c78cb2a         27.0          3.0         9.0   \n",
      "3  a9e4c62e71b897080f6e1e3c2929451e         15.0         13.0        10.0   \n",
      "4  ee0d8cc49ae4ef640ffd72f9153c0c54         23.0         26.0        22.0   \n",
      "\n",
      "   MB_ACTV_3M  MB_VIEW_1M  MB_VIEW_3M  MB_IS_OLD_USER  MB_IS_NEW_USER  \\\n",
      "0         7.0         0.0        21.0               1               0   \n",
      "1        23.0        22.0        14.0               1               0   \n",
      "2         5.0        22.0         8.0               1               0   \n",
      "3        18.0        12.0        27.0               1               0   \n",
      "4        15.0        24.0        24.0               1               0   \n",
      "\n",
      "   MB_IS_MID_USER  ...  MB_LOGIN_3M_NORM  MB_ACTV_1M_NORM  MB_ACTV_3M_NORM  \\\n",
      "0               0  ...          1.489596         0.206452        -0.921140   \n",
      "1               0  ...         -0.407268        -0.373469         0.986970   \n",
      "2               0  ...         -1.299910        -0.721422        -1.159654   \n",
      "3               0  ...         -0.184107        -0.605438         0.390685   \n",
      "4               0  ...          1.266436         0.786373         0.032915   \n",
      "\n",
      "   MB_VIEW_1M_NORM  MB_VIEW_3M_NORM  MB_LOGIN_STABILITY  MB_ACTV_STABILITY  \\\n",
      "0        -1.611379         0.774038            2.419355           5.100000   \n",
      "1         0.895362        -0.050409            4.285714           1.384615   \n",
      "2         0.895362        -0.757078           13.500000           3.375000   \n",
      "3        -0.244065         1.480708            2.812500           1.428571   \n",
      "4         1.123248         1.127373            2.379310           3.666667   \n",
      "\n",
      "   MB_VIEW_STABILITY  MB_OVERALL_STABILITY  MB_CCARD_POTENTIAL  \n",
      "0           0.000000              2.506452          228.979768  \n",
      "1           3.882353              3.184228          225.069024  \n",
      "2           6.000000              7.625000          209.056729  \n",
      "3           1.200000              1.813690          207.371966  \n",
      "4           2.666667              2.904215          262.107305  \n",
      "\n",
      "[5 rows x 77 columns]\n"
     ]
    }
   ],
   "source": [
    "print(\"=\"*100)\n",
    "print(\"特征预览\")\n",
    "print(\"=\"*100)\n",
    "\n",
    "print(\"\\n【自然属性特征】前5行:\")\n",
    "print(TRAIN_NATURE_features.head())\n",
    "\n",
    "print(\"\\n【资产负债特征】前5行:\")\n",
    "print(TRAIN_ASSET_features.head())\n",
    "\n",
    "print(\"\\n【产品持有特征】前5行:\")\n",
    "print(TRAIN_PROD_HOLD_features.head())\n",
    "\n",
    "print(\"\\n【掌银信息特征】前5行:\")\n",
    "print(TRAIN_MB_CUST_INFO_features.head())"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "starcup",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
